SpringSecurity(五):前后端分离认证总结案例。

前后端分离认证总结案例

    • 前言
    • 难点分析
    • Controller层
    • eneity层
      • Role
      • User
    • dao层
    • service层
    • config层
      • LoginFilter
      • SecurityConfig
    • resources
      • mapper
    • properties
    • pom.xml
    • 结尾

前言

和上一篇一样,从上倒下复制粘贴,所有代码贴完再运行,代码没有问题,只要贴对,都可以顺利跑出来的。

难点分析

前端系统给后端传递的数据为json,就会导致后端系统不能再用request.getParameter获取用户数据。
所以我们要将请求中json格式转换为对象,提取用户数据,然后进行认证。
在web传统项目进行认证请求时,底层调用的是FormLoginConfigurer,里面用UsernamePasswordAuthenticationFilter过滤器中的attempAuthentication试图认证的方法进行处理。下面看一下关键代码:

	if (this.postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}String username = obtainUsername(request);username = (username != null) ? username.trim() : "";String password = obtainPassword(request);password = (password != null) ? password : "";UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,password);

我们可以看到,在前后端分离项目中,上面获取用户名和密码的方式肯定是不可以的(基于request请求获取)。
所以我们就不能再用传统的formLogin中的AuthenticationFilter,我们 要把这个filter做一个重写。

重写的重点是如何获取参数的信息,而且要想有认证功能,最后还是要调用return this.getAuthenticationManager().authenticate(authRequest);这行代码。

实现的思路:
创建一个UsernamePasswordAuthenticationFilter的子类实现,让字类去替换attempAuthentication

我们确定了UsernamePasswordAuthenticationFilter不适合作为我们要用的过滤器,所以我们自己创建一个名为LoginFilter的过滤器。
但是security里面是有一系列的过滤器的,我们要确保LoginFilter替换UsernamePasswordAuthenticationFilter后位置不变。
请添加图片描述

Controller层

TestController方法:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class test {@GetMapping("/test")public String test(){System.out.println("test.....");return "test OK!";}
}

eneity层

Role

public class Role {private Integer id;private String name;private String nameZh;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getNameZh() {return nameZh;}public void setNameZh(String nameZh) {this.nameZh = nameZh;}
}

User

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.*;public class User implements UserDetails {private Integer id;private String username;private String password;private Boolean enabled;private Boolean accountNonExpired;private Boolean accountNonLocked;private Boolean credentialsNonExpired;private List<Role> roles = new ArrayList();@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {Set<GrantedAuthority> authorities = new HashSet();roles.forEach(role->{SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(role.getName());authorities.add(simpleGrantedAuthority);});return authorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return accountNonExpired;}@Overridepublic boolean isAccountNonLocked() {return accountNonLocked;}@Overridepublic boolean isCredentialsNonExpired() {return credentialsNonExpired;}@Overridepublic boolean isEnabled() {return enabled;}public void setRoles(List<Role> roles) {this.roles = roles;}public Integer getId() {return id;}public void setUsername(String username) {this.username = username;}public void setPassword(String password) {this.password = password;}public Boolean getEnabled() {return enabled;}public void setEnabled(Boolean enabled) {this.enabled = enabled;}public Boolean getAccountNonExpired() {return accountNonExpired;}public void setAccountNonExpired(Boolean accountNonExpired) {this.accountNonExpired = accountNonExpired;}public Boolean getAccountNonLocked() {return accountNonLocked;}public void setAccountNonLocked(Boolean accountNonLocked) {this.accountNonLocked = accountNonLocked;}public Boolean getCredentialsNonExpired() {return credentialsNonExpired;}public void setCredentialsNonExpired(Boolean credentialsNonExpired) {this.credentialsNonExpired = credentialsNonExpired;}public List<Role> getRoles() {return roles;}
}

dao层


import com.wang.entity.Role;
import com.wang.entity.User;
import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapper
public interface UserDao {//提供根据用户名返回方法User loadUserByUsername(String username);//提供根据用户id查询用户角色信息方法List<Role> getRoleByUid(Integer id);
}

service层


import com.wang.dao.UserDao;
import com.wang.entity.Role;
import com.wang.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;import java.util.List;@Service
public class MyUserDetailService implements UserDetailsService {private final UserDao userDao;@Autowiredpublic MyUserDetailService (UserDao userDao){this.userDao = userDao;}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//1. 查询用户User user = userDao.loadUserByUsername(username);if (ObjectUtils.isEmpty(user)) {throw new UsernameNotFoundException("用户名不存在");}//2. 查询权限信息List<Role> roles = userDao.getRoleByUid(user.getId());user.setRoles(roles);return user;}
}

config层

LoginFilter

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;public class LoginFilter extends UsernamePasswordAuthenticationFilter {public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {//1. 判断是否是post方式请求。if (!request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}//2. 判断是否是json格式请求类型。if(request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)){//3. 从json数据中获取用户输入用户名和密码进行认证{"uname":"xxx","password":"xxx"}try{Map<String,String> userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);String username = userInfo.get(getUsernameParameter());String password =userInfo.get(getPasswordParameter());System.out.println("用户名:"+username + "密码:"+password);UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);setDetails(request,authRequest);return this.getAuthenticationManager().authenticate(authRequest);} catch (IOException e) {e.printStackTrace();}}return super.attemptAuthentication(request,response);}
}

SecurityConfig


import com.fasterxml.jackson.databind.ObjectMapper;
import com.wang.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;import javax.print.attribute.standard.Media;
import java.util.HashMap;
import java.util.Map;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {private final MyUserDetailService myUserDetailService;@Autowiredpublic SecurityConfig(MyUserDetailService myUserDetailService){this.myUserDetailService = myUserDetailService;}
//    @Bean
//    public UserDetailsService userDetailsService() {
//        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
//        inMemoryUserDetailsManager.createUser(User.withUsername("root").password("{noop}123").roles("admin").build());
//        return inMemoryUserDetailsManager;
//    }@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(myUserDetailService);}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}//自定义filter交给工厂管理@Beanpublic LoginFilter loginFilter() throws Exception {LoginFilter loginFilter = new LoginFilter();loginFilter.setFilterProcessesUrl("/doLogin"); //指定认证的urlloginFilter.setUsernameParameter("uname"); //指定接受json 用户名keyloginFilter.setPasswordParameter("passwd"); //指定接受json 密码keyloginFilter.setAuthenticationManager(authenticationManagerBean());loginFilter.setAuthenticationSuccessHandler(((request, response, authentication) -> {Map<String, Object> result = new HashMap<>();result.put("msg", "登陆成功");result.put("用户信息", authentication.getPrincipal());response.setContentType("application/json;charset=UTF-8");response.setStatus(HttpStatus.OK.value());String s = new ObjectMapper().writeValueAsString(result);response.getWriter().println(s);})); //认证成功处理loginFilter.setAuthenticationFailureHandler(((request, response, exception) -> {Map<String, Object> result = new HashMap<>();result.put("msg", "登陆失败:" + exception.getMessage());response.setContentType("application/json;charset=UTF-8");response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());String s = new ObjectMapper().writeValueAsString(result);response.getWriter().println(s);})); //认证失败处理return loginFilter;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeHttpRequests().anyRequest().authenticated() //所有请求必须认证.and().formLogin().and().exceptionHandling().authenticationEntryPoint(((request, response, authException) -> {response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);response.setStatus(HttpStatus.UNAUTHORIZED.value());response.getWriter().println("请认证后再去处理!");})).and().logout().logoutRequestMatcher(new OrRequestMatcher(new AntPathRequestMatcher("/logout", HttpMethod.DELETE.name()),new AntPathRequestMatcher("/logout", HttpMethod.GET.name()))).logoutSuccessHandler(((request, response, authentication) -> {Map<String, Object> result = new HashMap<>();result.put("msg", "注销成功");result.put("用户信息", authentication.getPrincipal());response.setContentType("application/json;charset=UTF-8");response.setStatus(HttpStatus.OK.value());String s = new ObjectMapper().writeValueAsString(result);response.getWriter().println(s);})).and().csrf().disable();// at:用来某个filter替换过滤器链中的某个filter。http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);}
}

resources

mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wang.dao.UserDao"><!--        更具用户名查询用户方法--><select id="loadUserByUsername" resultType="com.wang.entity.User">select id,username,password,enabled,accountNonExpired,accountNonLocked,credentialsNonExpiredfrom userwhere username = #{username}</select><!--        查询指定⾏数据--><select id="getRoleByUid" resultType="com.wang.entity.Role">select r.id,r.name,r.name_zh nameZhfrom role r,user_role urwhere r.id = ur.ridand ur.uid = #{uid}</select>
</mapper>

properties

server.port= 9092
# 关闭thymeleaf 缓存
spring.thymeleaf.cache= false# 配置数据源
spring.datasource.type= com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name= com.mysql.cj.jdbc.Driver
spring.datasource.url= jdbc:mysql://localhost:3306/security?characterEncoding=UTF-8&useSSL=false&&serverTimezone=CST
spring.datasource.username= 你的账号
spring.datasource.password= 你的密码# Mybatis配置
# 注意mapper目录必须用"/"
mybatis.mapper-locations= classpath:com/wang/mapper/*.xml
mybatis.type-aliases-package=com.example.eneity# 日志处理
logging.level.com.example = debug

pom.xml

    <dependencies><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.18</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies>

结尾

所有代码都是测试过的,保证没有问题,如果存在错误,请仔细检查,欢迎留言讨论,结合编程不良人视频配套更佳。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/4839.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt QLineEdit篇

QLineEdit篇 【1】QLineEdit简介【2】QLineEdit常用方法【3】QLineEdit使用举例UI设计界面效果头文件源文件 PC饱和了&#xff0c;跟我学Qt比较实在&#xff0c;哈哈哈 【1】QLineEdit简介 QLineEdit是Qt框架中的一个类&#xff0c;用于创建一个文本输入框&#xff0c;允许用…

Google 将为高端 Chromebook 推出独立品牌

说起 Chromebook&#xff0c;一般大家的第一印象就是价格便宜、配置不高、做工普通&#xff0c;所选的材料也都是以塑料为主&#xff0c;产品主打的市场也是学生和教育群体。在不少人看来&#xff0c;Chromebook 就是一个配备了功能齐全的浏览器&#xff0c;外加一定的文件管理…

react antd 样式修改

最近在做一个大数据的大屏ui更改&#xff0c;使用的是antd&#xff0c;需要根据ui稿调很多的antd组件样式 特做一个样式修改记录&#xff0c;也给需要的人一些帮助 我们修改的有以下样式&#xff1a; 如何改呢&#xff1a; /*修改 antd 组件样式 */// 仅 drop 下的下拉框改变样…

苹果手机ios设备管理软件iMazing 2.17.6官方版下载及常见问题解决

苹果手机ios设备管理软件iMazing 2.17.6官方版下载(ios设备管理软件)是一款管理苹果设备的软件&#xff0c; Windows / macos 系统上的一款帮助用户管理 IOS 手机的应用程序&#xff0c;软件功能非常强大&#xff0c;界面简洁明晰、操作方便快捷&#xff0c;设计得非常人性化。…

命令模式(Command)

别名 动作&#xff08;Action&#xff09;事务&#xff08;Transaction&#xff09; 定义 命令是一种行为设计模式&#xff0c;它可将请求转换为一个包含与请求相关的所有信息的独立对象。该转换让你能根据不同的请求将方法参数化、延迟请求执行或将其放入队列中&#xff0c…

【PCB专题】Allegro如何设置电源电压属性,将飞线隐藏?

在PCB设计过程中,布局完成之后的布线的顺序一般是先走信号线,然后进行电源的处理、分割。因为电源往往在整个板子上是都有的,所以电源的飞线是非常多,在布线时特别影响其他信号线的布线,界面看着比较杂乱。 如下所示GND和1.8V都存在各种飞线,比较杂乱。 Allegro中有设置…

学习机组过程中的疑难问题与解决 -----(1)

本文章是在学习计算机组成原理过程中个人感觉需要理解与记忆的问题&#xff0c;还有一些在学习过程中自己产生的疑问以及解答,本文章可能排版不良&#xff0c;精力有限&#xff0c;还请见谅 第一章&#xff1a; &#xff08;1&#xff09;MAR的位数对应着存储单元的个数&#…

JVM学习整理(一)

一、JVM的基本介绍 JVM 是 Java Virtual Machine 的缩写&#xff0c;它是一个虚构出来的计算机&#xff0c;一种规范。通过在实际的计算机上仿真模拟各类计算机功能实现 好&#xff0c;其实抛开这么专业的句子不说&#xff0c;就知道JVM其实就类似于一台小电脑运行在windows或…

JSP网上订餐管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 网上订餐管理系统是一套完善的web设计系统&#xff0c;对理解JSP java SERLVET mvc编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&a…

Java微服务金融项目智牛股 项目简介与金融知识介绍及技术特点

项目简介 金融交易平台服务于金融衍生产品&#xff0c; 包含外汇、贵金属、期货、股票。 各产品具有不同属性与交易规则&#xff0c; 本项目对标MT4等大型交易平台&#xff0c; 遵循FIX全球最广泛的金融市场通用协议。 实现从证券注册开户、行情订阅与呈现&#xff0c; 股票撮合…

【GPT】如何拥有离线版本的GPT以及部署过程中的问题

【背景】 目前很多公司由于数据安全的问题&#xff0c;不能使用OpenAI的GPT&#xff0c;同时也没有必要非得使用如此泛用化的GPT。很多公司因此有训练自己的离线GPT的需求&#xff0c;这样的GPT只需要具备专业知识即可。 要使这个成为可能&#xff0c;首先就需要能够让GPT的Mo…

SAP ABAP 如果某字段没有参数ID,如自开发程序使用的自建表 新建参数ID

1&#xff09;新建参数ID sm30 TPARA 维护 输入ID和描述 2&#xff09; 参数ID和Se11数据元素 绑定