Spring Boot 集成 Spring Security 实现权限认证模块
作者:王帅@CodeSheep
写在前面
关于 Spring Security
Web系统的认证和权限模块也算是一个系统的基础设施了,几乎任何的互联网服务都会涉及到这方面的要求。在Java EE领域,成熟的安全框架解决方案一般有 Apache Shiro、Spring Security等两种技术选型。Apache Shiro简单易用也算是一大优势,但其功能还是远不如 Spring Security强大。Spring Security可以为 Spring 应用提供声明式的安全访问控制,起通过提供一系列可以在 Spring应用上下文中可配置的Bean,并利用 Spring IoC和 AOP等功能特性来为应用系统提供声明式的安全访问控制功能,减少了诸多重复工作。
关于JWT
JSON Web Token (JWT),是在网络应用间传递信息的一种基于 JSON的开放标准((RFC 7519),用于作为JSON对象在不同系统之间进行安全地信息传输。主要使用场景一般是用来在 身份提供者和服务提供者间传递被认证的用户身份信息。关于JWT的科普,可以看看阮一峰老师的《JSON Web Token 入门教程》。
本文则结合 Spring Security和 JWT两大利器来打造一个简易的权限系统。
本文实验环境如下:
Spring Boot版本:
2.0.6.RELEASEIDE:
IntelliJIDEA2018.2.4
另外本文实验代码置于文尾,需要自取。
设计用户和角色
本文实验为了简化考虑,准备做如下设计:
设计一个最简角色表
role,包括角色ID和角色名称
设计一个最简用户表
user,包括用户ID,用户名,密码
再设计一个用户和角色一对多的关联表
user_roles一个用户可以拥有多个角色
创建 Spring Security和 JWT加持的 Web工程
pom.xml中引入 Spring Security和 JWT所必需的依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency>
项目配置文件中加入数据库和 JPA等需要的配置
server.port=9991spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://121.196.XXX.XXX:3306/spring_security_jwt?useUnicode=true&characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=XXXXXXlogging.level.org.springframework.security=infospring.jpa.hibernate.ddl-auto=updatespring.jpa.show-sql=truespring.jackson.serialization.indent_output=true
创建用户、角色实体
用户实体 User:
/*** @ www.codesheep.cn* 20190312*/@Entitypublic class User implements UserDetails {@Id@GeneratedValueprivate Long id;private String username;private String password;@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)private List<Role> roles;...// 下面为实现UserDetails而需要的重写方法!@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> authorities = new ArrayList<>();for (Role role : roles) {authorities.add( new SimpleGrantedAuthority( role.getName() ) );}return authorities;}...}
此处所创建的 User类继承了 Spring Security的 UserDetails接口,从而成为了一个符合 Security安全的用户,即通过继承 UserDetails,即可实现 Security中相关的安全功能。
角色实体 Role:
/*** @ www.codesheep.cn* 20190312*/@Entitypublic class Role {@Id@GeneratedValueprivate Long id;private String name;... // 省略 getter和 setter}
创建JWT工具类
主要用于对 JWT Token进行各项操作,比如生成Token、验证Token、刷新Token等
/*** @ www.codesheep.cn* 20190312*/@Componentpublic class JwtTokenUtil implements Serializable {private static final long serialVersionUID = -5625635588908941275L;private static final String CLAIM_KEY_USERNAME = "sub";private static final String CLAIM_KEY_CREATED = "created";public String generateToken(UserDetails userDetails) {...}String generateToken(Map<String, Object> claims) {...}public String refreshToken(String token) {...}public Boolean validateToken(String token, UserDetails userDetails) {...}... // 省略部分工具函数}
创建Token过滤器,用于每次外部对接口请求时的Token处理
/*** @ www.codesheep.cn* 20190312*/@Componentpublic class JwtTokenFilter extends OncePerRequestFilter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Overrideprotected void doFilterInternal ( HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {String authHeader = request.getHeader( Const.HEADER_STRING );if (authHeader != null && authHeader.startsWith( Const.TOKEN_PREFIX )) {final String authToken = authHeader.substring( Const.TOKEN_PREFIX.length() );String username = jwtTokenUtil.getUsernameFromToken(authToken);if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(authToken, userDetails)) {UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authentication);}}}chain.doFilter(request, response);}}
Service业务编写
主要包括用户登录和注册两个主要的业务
public interface AuthService {User register( User userToAdd );String login( String username, String password );}
/*** @ www.codesheep.cn* 20190312*/@Servicepublic class AuthServiceImpl implements AuthService {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtTokenUtil jwtTokenUtil;@Autowiredprivate UserRepository userRepository;// 登录@Overridepublic String login( String username, String password ) {UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken( username, password );final Authentication authentication = authenticationManager.authenticate(upToken);SecurityContextHolder.getContext().setAuthentication(authentication);final UserDetails userDetails = userDetailsService.loadUserByUsername( username );final String token = jwtTokenUtil.generateToken(userDetails);return token;}// 注册@Overridepublic User register( User userToAdd ) {final String username = userToAdd.getUsername();if( userRepository.findByUsername(username)!=null ) {return null;}BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();final String rawPassword = userToAdd.getPassword();userToAdd.setPassword( encoder.encode(rawPassword) );return userRepository.save(userToAdd);}}
Spring Security配置类编写(非常重要)
这是一个高度综合的配置类,主要是通过重写 WebSecurityConfigurerAdapter 的部分 configure配置,来实现用户自定义的部分。
/*** @ www.codesheep.cn* 20190312*/@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled=true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserService userService;@Beanpublic JwtTokenFilter authenticationTokenFilterBean() throws Exception {return new JwtTokenFilter();}@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure( AuthenticationManagerBuilder auth ) throws Exception {auth.userDetailsService( userService ).passwordEncoder( new BCryptPasswordEncoder() );}@Overrideprotected void configure( HttpSecurity httpSecurity ) throws Exception {httpSecurity.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // OPTIONS请求全部放行.antMatchers(HttpMethod.POST, "/authentication/**").permitAll() //登录和注册的接口放行,其他接口全部接受验证.antMatchers(HttpMethod.POST).authenticated().antMatchers(HttpMethod.PUT).authenticated().antMatchers(HttpMethod.DELETE).authenticated().antMatchers(HttpMethod.GET).authenticated();// 使用前文自定义的 Token过滤器httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter
Spring Boot 集成 Spring Security 实现权限认证模块的更多相关文章
- Spring Boot集成Spring Data Reids和Spring Session实现Session共享
首先,需要先集成Redis的支持,参考:http://www.cnblogs.com/EasonJim/p/7805665.html Spring Boot集成Spring Data Redis+Sp ...
- SpringBoot系列:Spring Boot集成Spring Cache,使用EhCache
前面的章节,讲解了Spring Boot集成Spring Cache,Spring Cache已经完成了多种Cache的实现,包括EhCache.RedisCache.ConcurrentMapCac ...
- SpringBoot系列:Spring Boot集成Spring Cache,使用RedisCache
前面的章节,讲解了Spring Boot集成Spring Cache,Spring Cache已经完成了多种Cache的实现,包括EhCache.RedisCache.ConcurrentMapCac ...
- Spring boot 集成Spring Security
依赖jar <dependency> <groupId>org.springframework.cloud</groupId> <artifactId> ...
- Spring Boot 集成spring security4
项目GitHub地址 : https://github.com/FrameReserve/TrainingBoot Spring Boot (三)集成spring security,标记地址: htt ...
- Spring boot集成spring session实现session共享
最近使用spring boot开发一个系统,nginx做负载均衡分发请求到多个tomcat,此时访问页面会把请求分发到不同的服务器,session是存在服务器端,如果首次访问被分发到A服务器,那么se ...
- Spring Boot 集成 Spring Security
1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- Spring Boot 集成 Spring Security 入门案例教程
前言 本文作为入门级的DEMO,完全按照官网实例演示: 项目目录结构 Maven 依赖 <parent> <groupId>org.springframework.boot&l ...
- Spring Boot 集成 Spring Security 使用自定义的安全数据源
编写一个类自定义实现 UserDetailsService 接口 @Service("customUserDetailService") public class CustomUs ...
随机推荐
- 自动化测试 Appium之Python运行环境搭建 Part1
Appium之Python运行环境搭建 Part1 by:授客 QQ:1033553122 实践环境 Win7 Python 3.4.0 JAVA JDK 1.8.0_121 node.js8.11. ...
- 一起学Android之ListView
本文以一个小例子,简述Android开发中ListView的相关应用,仅供学习分享使用. 概述 ListView是一个显示可滚动项目列表的视图组(view group),列表项通过适配器(Adapte ...
- Handler,Looper,MessageQueue流程梳理
目的:handle的出现主要是为了解决线程间通讯. 举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在 ...
- 谈谈git以及如何关联github
git :一款免费.开源的分布式代码版本管理控制系统 记录当前产品代码的所有版本信息,包括历史修改信息 方便快速回退到某一个具体的版本 方便团队协作开发 可检测代码冲突.合并代码等 1.利用 git ...
- VSCode 下载Models 报错
VSCode调试部分代码时,报错,提示不能自动获取Models.报错信息如下. go: golang.org/x/crypto@v0.-80db560fac1f: unrecognized impor ...
- SQL Server数据库————增删改查
--增删改查--增 insert into 表名(列名) value(值列表) --删 delect from 表名 where 条件 --改 update 表名 set 列名=值1,列名2=值2 w ...
- 光盘安装win7系统教程
光盘安装系统是最传统的安装系统的方法,虽然现在U盘安装和硬盘安装已经很方便,但仍有很多用户习惯光盘安装的方式,下面小编教大家如何利用光盘安装系统. 来源:https://www.haoxitongx. ...
- 关于Knowledge Transfer的一点想法
维基百科中对于Knowledge Transfer(知识转移)的定义是: 知识转移是指分享或传播知识并为解决问题提供投入.在组织理论中,知识转移是将知识从组织的一个部分转移到另一个部分的实践问题. 与 ...
- win7系统IE浏览器主页被搜狗篡改问题的解决方法
IE浏览器使用一段时间后可能大家就会遇到主页被篡改的问题,篡改之后主页就变成了搜狗页面,我们常用的百度搜索也变成了搜狗搜索,这不仅使得我们操作起 来不习惯,使用起来也会感觉非常别扭.那如果在使用IE浏 ...
- 微信h5支付
分为 微信内H5调起支付 和 非微信浏览器H5支付. 1.H5支付(微信内) 参考链接:https://www.jianshu.com/p/6b9acdd10de6 2.JSAPI支付(非微信) 参考 ...