Spring-security整理

出于某些原因,需要学习一下spring的安全框架。(研究半天,如果单单说用户认证和授权这块儿,我感觉还是shiro好用。)

spring security介绍可以参考一下以下文档:

(满满的羡慕啊)我这里就不扯了。

http://www.tianshouzhi.com/api/tutorials/spring_security_4/252

一、先贴代码

下边是我的项目结构

Spring-security整理_第1张图片

当然我采用的是springboot + thymeleaf + mybatis + mysql

1.用到的pom.xml

 1         
 2             org.springframework.boot
 3             spring-boot-starter-web
 4         
 5 
 6         
 7             org.springframework.boot
 8             spring-boot-starter-test
 9             test
10         
11 
12         
13             org.mybatis.spring.boot
14             mybatis-spring-boot-starter
15             2.0.1
16         
17 
18 
19         
20             mysql
21             mysql-connector-java
22             5.1.32
23         
24 
25 
26         
27             org.projectlombok
28             lombok
29             1.18.8
30             provided
31         
32 
33 
34         
35             org.springframework.boot
36             spring-boot-starter-security
37             2.1.5.RELEASE
38         
39 
40         
41             org.springframework.boot
42             spring-boot-starter-thymeleaf
43 
44         
45         
46             org.thymeleaf.extras
47             thymeleaf-extras-springsecurity4
48             3.0.2.RELEASE
49         
pom.xml

2.核心配置类

 1 import org.springframework.context.annotation.Bean;
 2 import org.springframework.context.annotation.Configuration;
 3 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 4 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 5 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 6 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 7 import org.springframework.security.core.userdetails.UserDetailsService;
 8 import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
 9 
10 @Configuration
11 @EnableWebSecurity
12 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
13     @Bean
14     UserDetailsService customUserService(){ //注册UserDetailsService 的bean
15         return new UserDetailsServiceImpl();
16     }
17 
18     @Override
19     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
20         auth.userDetailsService(customUserService()).passwordEncoder(new MessageDigestPasswordEncoder("MD5")); //user Details Service验证
21     }
22     @Override
23     protected void configure(HttpSecurity http) throws Exception {
24         http.authorizeRequests()
25                 .anyRequest().authenticated() //authenticated任何请求,登录后可以访问
26                 .and()
27                 .csrf().disable()
28                 .formLogin()
29                 .loginPage("/user/login")//
30                 .defaultSuccessUrl("/user/index")
31                 .failureUrl("/user/login?error=true")
32                 .permitAll() //登录页面用户任意访问
33                 .and()
34                 .logout().permitAll(); //注销行为任意访问
35     }
36 }
WebSecurityConfig.java

3.UserDetailsServiceImpl

 1 import com.qx.demo.entity.RolePo;
 2 import com.qx.demo.entity.UserPo;
 3 import com.qx.demo.service.UserService;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 6 import org.springframework.security.core.userdetails.User;
 7 import org.springframework.security.core.userdetails.UserDetails;
 8 import org.springframework.security.core.userdetails.UserDetailsService;
 9 import org.springframework.security.core.userdetails.UsernameNotFoundException;
10 
11 import java.util.HashSet;
12 import java.util.Set;
13 
14 public class UserDetailsServiceImpl implements UserDetailsService {
15     @Autowired
16     UserService service;
17     @Override
18     public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
19         UserPo user = service.getUserById(s);
20         if (user==null){
21             throw new UsernameNotFoundException("用户名不存在");
22         }
23         Set authorities = new HashSet<>();
24         for (RolePo item:user.getRoles()){
25             authorities.add(new SimpleGrantedAuthority(item.getRName()));
26             System.out.println(item.getRName());
27         }
28         return new User(user.getUsername(),user.getPassword(),authorities);
29     }
30 }
UserDetailsServiceImpl.java

4.控制层Controller

 1 import com.qx.demo.service.UserService;
 2 import org.springframework.beans.factory.annotation.Autowired;
 3 import org.springframework.security.core.Authentication;
 4 import org.springframework.security.core.userdetails.User;
 5 import org.springframework.stereotype.Controller;
 6 import org.springframework.web.bind.annotation.RequestMapping;
 7 import org.springframework.web.servlet.ModelAndView;
 8 
 9 import javax.servlet.http.HttpServletRequest;
10 
11 @Controller
12 @RequestMapping("user")
13 public class UserController {
14     @Autowired
15     private UserService service;
16     @RequestMapping("login")
17     public ModelAndView toLogin(HttpServletRequest request){
18         ModelAndView mv= new ModelAndView("login");
19         String error = request.getParameter("error");
20         if (error!=null && error.equals("true")){
21             System.out.println("登录失败");
22         }
23         return mv;
24     }
25     @RequestMapping("index")
26     public String getUserById( Authentication authentication){
27         User principal = (User)authentication.getPrincipal();
28         if (authentication!=null)
29             System.out.println(authentication.getCredentials()+",\n"+authentication.getDetails()+",\n"+authentication.getPrincipal()+",\n"+authentication.getName());
30         return "index";
31     }
32 }
UserController.java

5.实体类

 1 import lombok.Data;
 2 import lombok.ToString;
 3 
 4 import java.util.List;
 5 
 6 @Data
 7 @ToString
 8 public class UserPo {
 9     private int uid;
10     private String username;
11     private String password;
12     private List roles;
13 }
UserPo.java
 1 import lombok.Data;
 2 import lombok.ToString;
 3 
 4 import java.util.List;
 5 
 6 @Data
 7 @ToString
 8 public class RolePo {
 9     private int rid;
10     private String rName;
11     private List pers;
12 }
RolePo.java
 1 import lombok.Data;
 2 import lombok.ToString;
 3 
 4 @Data
 5 @ToString
 6 public class PermissionPo {
 7     private int pid;
 8     private String pName;
 9     private String pPer;
10 }
PermissionPo

6.Dao层

1 import com.qx.demo.entity.UserPo;
2 
3 public interface UserMapper {
4     UserPo getUserByUsername(String username);
5 }
UserMapper.java
 1 
 2  3         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4 
 5 
 6     
 7         
 8         
 9         
10         
11             
12             
13             
14                 
15                 
16                 
17             
18         
19     
20     
28 
Mapper.xml

7.service层

1 import com.qx.demo.entity.UserPo;
2 
3 public interface UserService {
4     UserPo getUserById(String id);
5 }
UserService.java
 1 import com.qx.demo.dao.UserMapper;
 2 import com.qx.demo.entity.UserPo;
 3 import com.qx.demo.service.UserService;
 4 import org.springframework.stereotype.Service;
 5 
 6 import javax.annotation.Resource;
 7 
 8 @Service
 9 public class UserServiceImpl implements UserService {
10     @Resource
11     private UserMapper mapper;
12     @Override
13     public UserPo getUserById(String  username) {
14         return mapper.getUserByUsername(username);
15     }
16 }
UserServiceImpl.java

8.application.properties

 1 mybatis.mapper-locations=/mapper/*.xml
 2 mybatis.type-aliases-package=com.qx.demo.entity
 3 
 4 spring.datasource.driver-class-name=com.mysql.jdbc.Driver
 5 spring.datasource.url=jdbc:mysql:///qx
 6 spring.datasource.username=root
 7 spring.datasource.data-password=root
 8 
 9 spring.thymeleaf.mode=HTML5
10 spring.thymeleaf.cache=false
11 
12 logging.level.com.qx.demo.dao=debug
application.properties

二、再来看看spring-security的大概的执行流程

Spring-security整理_第2张图片

1.从上边图不难看出,spring-security的核心应该就是ProviderManager,但是ProviderManager并不直接执行认证和授权等相关操作,而是委托给AuthenticationProvider去完成相关操作,而对于每一个ProviderManager都是可以有多个AuthenticationProvider的。

2.AuthenticationProvider首先会找到UserDetailsService(有我们自己定义的)在数据库中查找用户信息。如果没有找到将会返回一个空的UserDetails,由于UserDetails是一个接口,我是在实体类中实现了该接口(那么这样我们就可以返回一个空壳user对象了)。

   当然,如果找到了我们可以直接授权并将UserDetailsService返回。

3.AuthenticationProvider就收到一个非空的UserDetailsService,接下来就该进行密码比对了。这时AuthenticationProvider将会调用PasswordEncoder进行密码比对。

4.无论成功与否,spring-security都会在对应的过滤器上找到响应的响应方式,并响应给客户。

 

Spring-security和shiro一样,都是基于过滤器,所以我们免不了去配置一些过滤器:

  Spring-security整理_第3张图片

当然,在前边的代码中已经体现过,该配置应该是在spring-security的核心配置类中进行配置(如果是xml也是一样的,大同小异嘛)。

上图是基于前后端分离,给前端返回一个json格式响应体的,比如登录成功时:

Spring-security整理_第4张图片

如图所示我们只需要实现AuthenticationSuccessHandler接口,并重写onAuthenticationSuccess方法即可。这个方法体完全就是一个类似与Servlet的方法体,有一定javaee基础的应该都没啥问题的。同样的道理如果登陆失败我们可以实现AuthenticationFailureHandler等等。

嗯,可能我忘了记录一个至关重要的玩意了,没错就是他:

Spring-security整理_第5张图片

你没看错,这就是一个基于用户名和密码的filter,spring-security拼什么这么智能,其实很简单就是一系列过滤器的使用。在源码中我们不难看出spring-security是设置了默认的登录地址的  /login,并且请求只允许POST请求。同时他也是spring-security默认登录过滤器。由于Java的开放原则的原因,你要是看他不爽你也可以自定义一个过滤器,你只需要继承一下AbstractAuthenticationProcessingFilter,重写其中的一些方法就好了。一般情况下我觉得默认的就可以了。

 

 

同样的与shiro雷同UsernamePasswordAuthenticationToken就是一个主体,当然保存的是客户端输入的登录信息。他最终是与UserDetailsService(在数据库中查到的信息)相对比得到登录结果。

 

对于用户信息来说我想密码应该是最重要的了,那么我们现在可以看看PasswordEncoder了

Spring-security整理_第6张图片

以上是security5中所有的PasswordEncoder了,当然,上边截图中有一个是我重写的PasswordEncoder(MyPasswordEncoder是我重写的)。这里我就不解释这些密码比对器都是什么作用了,如果有不了解的大家可以自己下去查询一下资料。我们公司要求的用的是MD5加密的方式,由于它是普通的散列,上网一百度就能看到答案,所以我需要重写一下PasswordEncoder。

Spring-security整理_第7张图片

同样的我们需要实现这个接口,我们需要重写两个方法encode()、matches()。一眼看上去不难发现,matches一定是用户认证的,那我们就可以把重点放到这个方法下了。

Spring-security整理_第8张图片

因为是例子,所以我写的比较简单了,显而易见我用的Spring自带的MD5加密工具了。

当然,spring-security是比较推荐 BCryptPasswordEncoder,我也象征性的看了一下源码并不是很难理解大家可以去看下。

 

转载于:https://www.cnblogs.com/Tiandaochouqin1/p/10979063.html

你可能感兴趣的:(数据库,前端,json)