springboot+springsecurity+JWT

目录结构: springbootApplication里面的配置,如有需要可以自行添加  实现JWT功能 在pom.xml中的配置 1 <?xml version="1.0" encoding="UTF-8"?> 2...

目录结构:

springbootApplication里面的配置,如有需要可以自行添加

 实现JWT功能

在pom.xml中的配置

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <project xmlns="http://maven.apache.org/POM/4.0.0"
  3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5     <modelVersion>4.0.0</modelVersion>
  6 
  7     <parent>
  8         <groupId>org.springframework.boot</groupId>
  9         <artifactId>spring-boot-starter-parent</artifactId>
 10         <version>2.1.5.RELEASE</version>
 11     </parent>
 12 
 13     <groupId>com.my</groupId>
 14     <artifactId>my-springsecurity</artifactId>
 15     <version>1.0-SNAPSHOT</version>
 16 
 17 
 18     <dependencies>
 19         <dependency>
 20             <groupId>org.springframework.boot</groupId>
 21             <artifactId>spring-boot-starter-web</artifactId>
 22         </dependency>
 23         <dependency>
 24             <groupId>org.springframework.boot</groupId>
 25             <artifactId>spring-boot-starter-thymeleaf</artifactId>
 26         </dependency>
 27         <dependency>
 28             <groupId>org.springframework.boot</groupId>
 29             <artifactId>spring-boot-starter-jdbc</artifactId>
 30         </dependency>
 31         <dependency>
 32             <groupId>org.springframework.data</groupId>
 33             <artifactId>spring-data-redis</artifactId>
 34             <version>2.1.5.RELEASE</version>
 35         </dependency>
 36         <dependency>
 37             <groupId>redis.clients</groupId>
 38             <artifactId>jedis</artifactId>
 39         </dependency>
 40         <dependency>
 41             <groupId>org.springframework.boot</groupId>
 42             <artifactId>spring-boot-test</artifactId>
 43             <scope>test</scope>
 44         </dependency>
 45         <!-- oracle数据库驱动 -->
 46         <dependency>
 47             <groupId>com.oracle</groupId>
 48             <artifactId>ojdbc6</artifactId>
 49             <version>11.2.0.3</version>
 50         </dependency>
 51         <dependency>
 52             <groupId>org.mybatis.spring.boot</groupId>
 53             <artifactId>mybatis-spring-boot-starter</artifactId>
 54             <version>1.3.2</version>
 55         </dependency>
 56 
 57         <dependency>
 58             <groupId>org.springframework.boot</groupId>
 59             <artifactId>spring-boot-starter-security</artifactId>
 60         </dependency>
 61         <dependency>
 62             <groupId>org.springframework.security</groupId>
 63             <artifactId>spring-security-web</artifactId>
 64             <version>5.0.4.RELEASE</version>
 65         </dependency>
 66         <dependency>
 67             <groupId>org.springframework.security</groupId>
 68             <artifactId>spring-security-config</artifactId>
 69             <version>5.0.4.RELEASE</version>
 70         </dependency>
 71         <dependency>
 72             <groupId>io.jsonwebtoken</groupId>
 73             <artifactId>jjwt</artifactId>
 74             <version>0.9.0</version>
 75         </dependency>
 76 
 77         <dependency>
 78             <groupId>com.alibaba</groupId>
 79             <artifactId>fastjson</artifactId>
 80             <version>1.2.30</version>
 81         </dependency>
 82         <dependency>
 83             <groupId>org.springframework.boot</groupId>
 84             <artifactId>spring-boot-starter-test</artifactId>
 85             <scope>test</scope>
 86         </dependency>
 87     </dependencies>
 88 
 89     <build>
 90         <finalName>${project.artifactId}</finalName>
 91         <plugins>
 92             <!-- &lt;!&ndash; 资源文件拷贝插件 &ndash;&gt;
 93              <plugin>
 94                  <groupId>org.apache.maven.plugins</groupId>
 95                  <artifactId>maven-resources-plugin</artifactId>
 96                  <configuration>
 97                      <encoding>UTF-8</encoding>
 98                  </configuration>
 99              </plugin>-->
100             <!-- java编译插件 -->
101             <plugin>
102                 <groupId>org.apache.maven.plugins</groupId>
103                 <artifactId>maven-compiler-plugin</artifactId>
104                 <configuration>
105                     <source>1.8</source>
106                     <target>1.8</target>
107                     <encoding>UTF-8</encoding>
108                 </configuration>
109             </plugin>
110             <plugin>
111                 <groupId>org.springframework.boot</groupId>
112                 <artifactId>spring-boot-maven-plugin</artifactId>
113             </plugin>
114         </plugins>
115     </build>
116 </project>
maven

 在application.yml中的配置

 1 server:
 2   port: ${PORT:8080}
 3 spring:
 4   application:
 5     name: my-springsecurity
 6   datasource:
 7     url: jdbc:oracle:thin:@localhost:1521:orcl
 8     username: scott
 9     password: oracle
10     driver-class-name: oracle.jdbc.driver.OracleDriver
11   redis:
12     host: ${REDIS_HOST:127.0.0.1}
13     port: ${REDIS_PORT:6379}
14     timeout: 5000 #连接超时 毫秒
15 #  main:
16 #    allow-bean-definition-overriding: true #同名bean覆盖
17   security:
18     loginType: JSONs
19 
20 # 无法扫描到resources下的templates中的静态文件时可以配置,如果可以无需配置
21 #注入到TomcatConfig
22 bw:
23   factory:
24     doc:
25       root: E:JAVAIDEAMyspringbootmy-springsecuritysrcmainresourcestemplates
application.yml

首先需要继承WebSecurityConfigurerAdapter类,配置security中的认证授权相关配置

 1 @EnableWebSecurity
 2 @EnableGlobalMethodSecurity(prePostEnabled = true)
 3 public class SecurityConfig extends WebSecurityConfigurerAdapter {
 4 
 5     //token 过滤器,解析token
 6     @Autowired
 7     MyJwtTokenFilter jwtTokenFilter;
 8 
 9     
10     @Autowired
11     MyUserDetailsService myUserDetailsService;
12 
13     @Autowired
14     private SendSmsSecurityConfig sendSmsSecurityConfig;
15 
16     //加密机制
17     @Bean
18     public PasswordEncoder passwordEncoder() {
19         return NoOpPasswordEncoder.getInstance();
20     }
21 
22 
23     //认证
24     @Override
25     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
26         auth.userDetailsService(myUserDetailsService)
27                 .passwordEncoder(passwordEncoder());
28     }
29 
30     @Override
31     protected void configure(HttpSecurity http) throws Exception {
32         http.authorizeRequests()
33 //                .antMatchers("/admin/api/**").hasRole("ADMIN")
34 //                .antMatchers("/user/api/**").hasRole("USER")
35                 .antMatchers("/SendSms").permitAll()
36                 .antMatchers("/**").permitAll()
37                 .anyRequest().authenticated()//任何请求登录后访问
38                 .and()
39                 // 基于token,所以不需要session
40                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
41 //                .sessionManagement() //会话管理
42 //                .maximumSessions(1); //限制登录人数
43                 .and().csrf().disable();
44 
45         //登录操作
46         http.formLogin()
47                 .loginPage("/login.html")//登录路径
48                 .loginProcessingUrl("/farmerlogin")//登录表单提交请求
49                 .usernameParameter("username")//设置登录账号参数,默认username
50                 .passwordParameter("password")//设置登录密码参数,默认password
51 //                .defaultSuccessUrl("/home.html")//登录成功跳转,不能和successHandler一起使用
52 //                .failureUrl("/login.html")// 登录失败跳转,不能和failureHandler一起使用
53                 .permitAll()
54                 //登录成功处理
55                 .successHandler(new LoginSuccessHandler())
56                 //登录失败处理
57                 .failureHandler(new LoginFailureHandler())
58                 .and().apply(sendSmsSecurityConfig);
59 
60 
61         //退出操作
62         http.logout()
63                 .logoutUrl("/aaa")//退出提交参数
64                 .logoutSuccessUrl("/login.html");//退出后跳转,不能和logoutSuccessHandler一起使用
65 //        .logoutSuccessHandler();//退出处理
66 
67         // 禁用缓存
68         http.headers().cacheControl();
69 
70         http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
71                 // 添加权限不足 filter
72                 .exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler())
73                 //其他异常处理类
74                 .authenticationEntryPoint(new MyAuthenticationException());
75     }
76 
77     //忽略拦截
78     @Override
79     public void configure(WebSecurity web) throws Exception {
80         web.ignoring().antMatchers("/css/**", "/elementuidemo/**",
81                 "/img/**", "/js/**", "/plugins/**", "/static/json/**", "/pages/**");
82     }
83     
84 }
WebSecurityConfigurerAdapter的配置类

配置相关配件:

登录成功处理:继承SavedRequestAwareAuthenticationSuccessHandler类,该类继承AuthenticationSuccessHandler类,AuthenticationSuccessHandler登录成功父类,之所以用SavedRequestAwareAuthenticationSuccessHandler类,里面可以实现其他的方法,如:登录成功跳转到登录之前请求的页面

 1 /**
 2  * <p>
 3  * AuthenticationSuccessHandler登录成功父类
 4  * SavedRequestAwareAuthenticationSuccessHandler 继承AuthenticationSuccessHandler
 5  * <p>
 6  * 登录成功处理
 7  */
 8 @Component
 9 public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
10 
11 /*    @Value("${spring.security.loginType}")
12     private String loginType;*/
13 
14 /*    @Autowired
15     JwtTokenUtil jwtTokenUtil;*/
16 
17     @Override
18     public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
19         System.out.println("登录成功");
20         //从authentication中获取用户信息
21         final User farmerDetail = (User) authentication.getPrincipal();
22         JwtTokenUtil jwtTokenUtil = new JwtTokenUtil();
23         //生成jwt
24         String token = jwtTokenUtil.generateToken(farmerDetail);
25         httpServletResponse.addHeader("token", "Bearer " + token);
26 
27 /*        if (loginType.equalsIgnoreCase("JSON")) {
28         httpServletResponse.setContentType("application/json");
29         httpServletResponse.setCharacterEncoding("UTF-8");
30             httpServletResponse.getWriter().write("{"result":"ok"}");
31 //        httpServletResponse.getWriter().flush();
32         } else {
33             //跳转到登录之前请求的页面
34             super.onAuthenticationSuccess(httpServletRequest, httpServletResponse, authentication);
35         }*/
36         httpServletResponse.setContentType("application/json");
37         httpServletResponse.setCharacterEncoding("UTF-8");
38         httpServletResponse.getWriter().write("{"result":"ok"}");
39 
40     }
41 }
LoginSuccessHandler类

登录失败处理:继承SimpleUrlAuthenticationFailureHandler类,该类继承SimpleUrlAuthenticationFailureHandler类,SimpleUrlAuthenticationFailureHandler类登录失败父类,之所以用SimpleUrlAuthenticationFailureHandler类,里面可以实现其他方法,如:登录失败跳转到登录页面

 1 /**
 2  *
 3  * AuthenticationFailureHandler登录失败父类
 4  * SimpleUrlAuthenticationFailureHandler 继承AuthenticationFailureHandler
 5  *
 6  * 登录失败处理
 7  */
 8 @Component
 9 public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {
10 
11    /* @Value("${spring.security.loginType}")
12     private String loginType;*/
13 
14     @Override
15     public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
16 /*        if (loginType.equalsIgnoreCase("JSON")) {
17         httpServletResponse.setContentType("application/json;charset=UTF-8");
18         httpServletResponse.setStatus(401);
19         PrintWriter out = httpServletResponse.getWriter();
20         out.write("{"error_code":"401","name":""+e.getClass()+"","message":""+e.getMessage()+""}");
21 
22         }else {
23             //登录失败跳转到登录页面
24             super.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
25         }*/
26 
27         httpServletResponse.setContentType("application/json;charset=UTF-8");
28         httpServletResponse.setStatus(401);
29         PrintWriter out = httpServletResponse.getWriter();
30         out.write("{"error_code":"401","name":""+e.getClass()+"","message":""+e.getMessage()+""}");
31 
32     }
33 }
LoginFailureHandler类

权限不足处理:实现的AccessDeniedHandler类,进行的权限校验

 1 /**
 2  * Spring security权限不足处理类
 3  * 只有登录后(即接口有传token)接口权限不足才会进入AccessDeniedHandler,
 4  * 如果是未登陆或者会话超时等,不会触发AccessDeniedHandler,而是会直接跳转到登陆页面。
 5  */
 6 @Component
 7 public class MyAccessDeniedHandler implements AccessDeniedHandler {
 8     @Override
 9     public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
10         //登陆状态下,权限不足执行该方法
11         System.out.println("权限不足:" + e.getMessage());
12         response.setStatus(200);
13         response.setCharacterEncoding("UTF-8");
14         response.setContentType("application/json; charset=utf-8");
15         PrintWriter printWriter = response.getWriter();
16         response.getWriter().write("{"result":"权限不足"}");
17         printWriter.flush();
18     }
19 }
MyAccessDeniedHandler类

异常处理:实现AuthenticationEntryPoint类,实现异常的处理,如果不配置此类,则spring security默认会跳转到登录页面

 1 /**
 2  * Spring security其他异常处理类,比如请求路径不存在等,
 3  * 如果不配置此类,则Spring security默认会跳转到登录页面
 4  */
 5 @Component
 6 public class MyAuthenticationException implements AuthenticationEntryPoint {
 7     @Override
 8     public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
 9         System.out.println("AuthenticationEntryPoint检测到异常:"+e);
10         httpServletResponse.setStatus(200);
11         httpServletResponse.setCharacterEncoding("UTF-8");
12         httpServletResponse.setContentType("application/json; charset=utf-8");
13         PrintWriter printWriter = httpServletResponse.getWriter();
14         httpServletResponse.getWriter().write("AuthenticationEntryPoint检测到异常:"+e);
15         printWriter.flush();
16     }
17 }
MyAuthenticationException类

UserDetailsService验证用户名、密码和授权处理,实现从数据库查询用户功能

 1 /**
 2  * 根据账号查询用户
 3  */
 4 @Component
 5 public class MyUserDetailsService implements UserDetailsService {
 6 
 7     @Autowired
 8     private UserMapper userMapper;
 9 
10 
11 
12     @Override
13     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
14 
15         //从数据库读取该用户
16         User user = userMapper.findByUserName(username);
17         // 用户不存在,抛出异常
18         if (user == null){
19             throw new UsernameNotFoundException("用户不存在");
20         }
21         //将数据库形式的roles解析为UserDtails的权限集
22 //        farmer.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(farmer.getRoles()));
23         List<GrantedAuthority> authorities = generateAuthorities(user.getRoles());
24         user.setAuthorities(authorities);
25         return user;
26 
27         //基于内存验证用户信息
28 //        UserDetails userDetails = Farmer.withUsername("123").password("123").roles("USER").build();
29 //        return userDetails;
30     }
31 
32     //自定义实现权限转换
33     private List<GrantedAuthority> generateAuthorities(String roles){
34         List<GrantedAuthority> authorities = new ArrayList<>();
35         String[] roleArray = roles.split(",");
36         if (roles != null && !"".equals(roles)){
37             for (String role : roleArray) {
38                 authorities.add(new SimpleGrantedAuthority(role));
39             }
40         }
41         return authorities;
42     }
43 }
MyUserDetailsService类

token过滤器进行解析判断是否登录,继承OncePerRequestFilter类

 1 /**
 2  * token 过滤器,在这里解析token,拿到该用户角色,设置到springsecurity的上下文环境中,让springsecurity自动判断权限
 3  * 所有请求最先进入此过滤器,包括登录接口,而且在springsecurity的密码验证之前执行
 4  */
 5 @Component
 6 public class MyJwtTokenFilter extends OncePerRequestFilter {
 7 
 8     @Autowired
 9     MyUserDetailsService myUserDetailsService;
10     @Autowired
11     private JwtTokenUtil jwtTokenUtil;
12 
13 
14     @Override
15     protected void doFilterInternal(HttpServletRequest request,
16                                     HttpServletResponse response,
17                                     FilterChain chain)
18             throws ServletException, IOException {
19 
20         String authHeader = request.getHeader("Authorization");
21         String tokenHead = "Bearer ";
22 
23         if (authHeader != null && authHeader.startsWith(tokenHead)) {
24             String authToken = authHeader.substring(tokenHead.length());
25             String username = jwtTokenUtil.getUsernameFromToken(authToken);
26             if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
27                 UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username);
28                 //验证令牌是否有效
29                 if (jwtTokenUtil.validateToken(authToken, userDetails)) {
30                     UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
31                     authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
32                     SecurityContextHolder.getContext().setAuthentication(authentication);
33                 }
34             }
35         }
36         chain.doFilter(request, response);
37     }
38 }
MyJwtTokenFilter类

JWT工具类

  1 @Component
  2 public class JwtTokenUtil implements Serializable {
  3     /**
  4      * 密钥
  5      */
  6     private final String secret = "aaaaaaaa";
  7 
  8     /**
  9      * 从数据声明生成令牌
 10      *
 11      * @param claims 数据声明
 12      * @return 令牌
 13      */
 14     private String generateToken(Map<String, Object> claims) {
 15         Date expirationDate = new Date(System.currentTimeMillis() + 2592000L * 1000);
 16         return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
 17     }
 18 
 19     /**
 20      * 从令牌中获取数据声明
 21      *
 22      * @param token 令牌
 23      * @return 数据声明
 24      */
 25     private Claims getClaimsFromToken(String token) {
 26         Claims claims;
 27         try {
 28             claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
 29         } catch (Exception e) {
 30             claims = null;
 31         }
 32         return claims;
 33     }
 34 
 35     /**
 36      * 生成令牌
 37      *
 38      * @param userDetails 用户
 39      * @return 令牌
 40      */
 41     public String generateToken(UserDetails userDetails) {
 42         Map<String, Object> claims = new HashMap<>(2);
 43         claims.put("sub", userDetails.getUsername());
 44         claims.put("created", new Date());
 45         return generateToken(claims);
 46     }
 47 
 48     /**
 49      * 从令牌中获取用户名
 50      *
 51      * @param token 令牌
 52      * @return 用户名
 53      */
 54     public String getUsernameFromToken(String token) {
 55         String username;
 56         try {
 57             Claims claims = getClaimsFromToken(token);
 58             username = claims.getSubject();
 59         } catch (Exception e) {
 60             username = null;
 61         }
 62         return username;
 63     }
 64 
 65     /**
 66      * 判断令牌是否过期
 67      *
 68      * @param token 令牌
 69      * @return 是否过期
 70      */
 71     
                        
  • 发表于 2020-05-07 16:28
  • 阅读 ( 165 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除