关于什么是jwt(json web token),还有jwt的工作流程我这边就不多介绍,主要给大家介绍一下SpringBoot中如何整合Security然后在添加jwt的支持。
想学习分布式、微服务、JVM、多线程、架构、java、python的童鞋,千万不要扫码,否则后果自负~
通过SpringBoot+Security+JWT来实现token校验的过程。在生产环境中,对发布API增加授权保护是非常必要的一件事情,这里我们就是利用jwt生成token,利用token来进行接口安全验证。
在整合jwt之前,我们首先要在SpringBoot中整合security的模块,来实现基于security的授权控制。用过security的人都知道,它的功能无比的强大比shiro还要强大,但是今天我就介绍security中关于权限控制和是登录验证的这一块的功能。
1.在开始之前我们先在pom.xml中添加security和jwt的相关依赖,如下所示:
4.0.0
org.sang
test26-security
0.0.1-SNAPSHOT
jar
Test26-Security
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.4.3.RELEASE
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-test
test
org.thymeleaf.extras
thymeleaf-extras-springsecurity4
mysql
mysql-connector-java
5.1.40
io.jsonwebtoken
jjwt
0.9.0
org.springframework.boot
spring-boot-maven-plugin
2.在application.properties中添加数据库和jpa还有jwt的相关配置。具体如下所示:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/security_db?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=220316
logging.level.org.springframework.security=info
spring.thymeleaf.cache=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
# JWT 604800
jwt.header:Authorization
jwt.secret:mySecret
jwt.expiration:10
jwt.tokenHead:Bearer
jwt.route.authentication.path:auth/login
jwt.route.authentication.refresh:auth/refresh
jwt.route.authentication.register:"auth/register"
3.新建实体类,进行用户登录的授权验证。(这里我新建两个实体类,一个是用户实体类,一个是用户关系实体类)
用户实体类:SysUser
package org.security.entity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
@Entity
public class SysUser implements UserDetails {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
private Date lastPasswordResetDate;
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
private List roles;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public List getRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
public Date getLastPasswordResetDate() {
return lastPasswordResetDate;
}
public void setLastPasswordResetDate(Date lastPasswordResetDate) {
this.lastPasswordResetDate = lastPasswordResetDate;
}
@Override
public Collection extends GrantedAuthority> getAuthorities() {
List auths = new ArrayList<>();
List roles = this.getRoles();
for (SysRole role : roles) {
auths.add(new SimpleGrantedAuthority(role.getName()));
}
return auths;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
因为SysUser实体类继承UserDetails接口,这个类是基于Security的安全服务。所以通过继承UserDetails这个类,我们就可以实现Security中相关的功能了,后面我们会具体介绍是怎么一回事。
用户关系实体类:SysRole
package org.security.entity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class SysRole {
@Id
@GeneratedValue
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SysRole实体类是保存着用户的拥有什么样权限的实体类。和用户实体是多对一的关系,也就是一个用户可以拥有多个权限。
实体类创建好之后,我们就可以编写相应的dao,service,controller类来实现用户登录的功能了。这里我会新建一个login.html页面和index.html页面,来进行登录校验的和权限控制演示。
dao接口(这里我采用jpa来实现用户查询的功能)
package org.security.dao;
import org.security.entity.SysUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SysUserRepository extends JpaRepository {
SysUser findByUsername(String username);
}
service业务类
package org.security.service;
import org.security.dao.SysUserRepository;
import org.security.entity.SysUser;
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;
public class CustomUserService implements UserDetailsService {
@Autowired
SysUserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
SysUser user = userRepository.findByUsername(s);
if (user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
System.out.println("s:"+s);
System.out.println("username:"+user.getUsername()+";password:"+user.getPassword());
System.out.println("size:"+user.getRoles().size());
System.out.println("role:"+user.getRoles().get(0).getName());
return user;
}
}
controller控制类:
package org.security.controller;
import org.security.entity.Msg;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* Created by linzhiqiang on 2017/10/26
*/
@Controller
public class HomeController {
@RequestMapping("/")
public String index(Model model) {
Msg msg = new Msg("测试标题", "测试内容", "额外信息,只对管理员显示");
model.addAttribute("msg", msg);
return "index";
}
@PreAuthorize("hasAuthority('ROLE_USER')")
@RequestMapping(value="/admin/test1")
@ResponseBody
public String adminTest1() {
return "ROLE_USER";
}
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@RequestMapping("/admin/test2")
@ResponseBody
public String adminTest2() {
return "ROLE_ADMIN";
}
}
login.html页面:
登录
已注销
有错误,请重试
使用账号密码登录
index.html页面:
无更多显示信息
4.SpringBoot中配置基于security的支持,这里我有两个配置
一个是没有登录用户都跳转到login.html页面的配置:
package org.security.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
接下来就是security核心的配置类了:
package org.security.config;
import org.security.service.CustomUserService;
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.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
protected UserDetailsService customUserService() {
return new CustomUserService();
}
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
// 设置UserDetailsService
.userDetailsService(customUserService())
// 使用BCrypt进行密码的hash
.passwordEncoder(passwordEncoder());
}
// 装载BCrypt密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// 由于使用的是JWT,我们这里不需要csrf
// .csrf().disable()
// 基于token,所以不需要session
// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
//.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 允许对于网站静态资源的无授权访问
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
// 对于获取token的rest api要允许匿名访问
.antMatchers("/auth/**").permitAll()
.antMatchers("/admin/**").hasIpAddress("127.0.0.1")
.antMatchers("/admin/**").access("hasAuthority('ROLE_ADMIN')")
.anyRequest().authenticated().and().formLogin().loginPage("/login")
.failureUrl("/login?error").permitAll().and().logout().permitAll();
// 除上面外的所有请求全部需要鉴权认证
// .anyRequest().authenticated();
// 添加JWT filter
// httpSecurity
// .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// 禁用缓存
// httpSecurity.headers().cacheControl();
}
}
这里我暂时先把jwt相关的代码先注释掉,将注解取消掉的话,就可以实现jwt相关的功能了。
5.打开浏览器进行功能测试,下面是测试的结果:
如果未登录的话,就都会跳转到这个login.html登录页面
登录成功后进入的index页面,这里测试的账户权限类型是admin,下面有两个地址一个是admin/test1只能user权限才可以访问,还有一个是admin/test2拥有admin权限的人才可以访问,下面我们开始测试。
因为权限不够所以,返回的是403错误。
权限够的话,返回的是这个接口获取的数据。
到这里关于SpringBoot整合Security就介绍完了,接下来就是如何添加jwt的支持了。
6.基本过程和security差不多:一:首先创建一个工具类,用来生成jwt的token和token的验证刷新之类的。
二:然后创建一个fiter类,并配置注入security配置文件中
三:接下来就是创建dao,service,controller来实现接口。
四:最后就是利用postman来测试是否实现jwt功能
工具类JwtTokenUtil:
package org.security.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.security.entity.SysUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtTokenUtil implements Serializable {
private static final long serialVersionUID = -3301605591108950415L;
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String getUsernameFromToken(String token) {
String username;
try {
final Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
public Date getCreatedDateFromToken(String token) {
Date created;
try {
final Claims claims = getClaimsFromToken(token);
created = new Date((Long) claims.get(CLAIM_KEY_CREATED));
} catch (Exception e) {
created = null;
}
return created;
}
public Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
return (lastPasswordReset != null && created.before(lastPasswordReset));
}
public String generateToken(UserDetails userDetails) {
Map claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
String generateToken(Map claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
final Date created = getCreatedDateFromToken(token);
return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
&& !isTokenExpired(token);
}
public String refreshToken(String token) {
String refreshedToken;
try {
final Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
public Boolean validateToken(String token, UserDetails userDetails) {
SysUser user = (SysUser) userDetails;
final String username = getUsernameFromToken(token);
final Date created = getCreatedDateFromToken(token);
// final Date expiration = getExpirationDateFromToken(token);
return (
username.equals(user.getUsername())
&& !isTokenExpired(token)
&& !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()));
}
}
dao接口:
package org.security.dao;
import org.security.entity.SysUser;
public interface AuthService {
SysUser register(SysUser userToAdd);
String login(String username, String password);
String refresh(String oldToken);
}
dao接口实现类(service业务类):
package org.security.service;
import org.security.dao.AuthService;
import org.security.dao.SysUserRepository;
import org.security.entity.SysUser;
import org.security.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Date;
import static java.util.Arrays.asList;
@Service
public class AuthServiceImpl implements AuthService {
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
private JwtTokenUtil jwtTokenUtil;
private SysUserRepository userRepository;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
public AuthServiceImpl(
AuthenticationManager authenticationManager,
UserDetailsService userDetailsService,
JwtTokenUtil jwtTokenUtil,
SysUserRepository userRepository) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
this.jwtTokenUtil = jwtTokenUtil;
this.userRepository = userRepository;
}
@Override
public SysUser register(SysUser 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));
userToAdd.setLastPasswordResetDate(new Date());
// userToAdd.setRoles(asList("ROLE_USER"));
return userRepository.save(userToAdd);
}
@Override
public String login(String username, String password) {
UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
// Perform the security
final Authentication authentication = authenticationManager.authenticate(upToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
// Reload password post-security so we can generate token
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
final String token = jwtTokenUtil.generateToken(userDetails);
return token;
}
@Override
public String refresh(String oldToken) {
final String token = oldToken.substring(tokenHead.length());
String username = jwtTokenUtil.getUsernameFromToken(token);
SysUser user = (SysUser) userDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())){
return jwtTokenUtil.refreshToken(token);
}
return null;
}
}
controller控制类:
package org.security.controller;
import org.security.dao.AuthService;
import org.security.entity.SysUser;
import org.security.service.JwtAuthenticationRequest;
import org.security.service.JwtAuthenticationResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@RestController
public class AuthController {
@Value("${jwt.header}")
private String tokenHeader;
@Autowired
private AuthService authService;
@RequestMapping(value = "/auth/login", method = RequestMethod.POST)
public ResponseEntity> createAuthenticationToken(
String username,String password
) throws AuthenticationException{
// @RequestBody JwtAuthenticationRequest authenticationRequest
final String token = authService.login(username,password);
// Return the token
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
@RequestMapping(value = "/auth/refresh", method = RequestMethod.GET)
public ResponseEntity> refreshAndGetAuthenticationToken(
HttpServletRequest request) throws AuthenticationException{
String token = request.getHeader(tokenHeader);
String refreshedToken = authService.refresh(token);
if(refreshedToken == null) {
return ResponseEntity.badRequest().body(null);
} else {
return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken));
}
}
@RequestMapping(value = "${jwt.route.authentication.register}", method = RequestMethod.POST)
public SysUser register(@RequestBody SysUser addedUser) throws AuthenticationException{
return authService.register(addedUser);
}
}
接在来就是最重要的filter类:
package org.security.config;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.security.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Value("${jwt.header}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(this.tokenHeader);
if (authHeader != null && authHeader.startsWith(tokenHead)) {
final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
String username = jwtTokenUtil.getUsernameFromToken(authToken);
logger.info("checking authentication " + username);
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));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}
将security中配置文件注解去掉,去掉之后如下所示:
package org.security.config;
import org.security.service.CustomUserService;
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.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
protected UserDetailsService customUserService() {
return new CustomUserService();
}
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
// 设置UserDetailsService
.userDetailsService(customUserService())
// 使用BCrypt进行密码的hash
.passwordEncoder(passwordEncoder());
}
// 装载BCrypt密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService());
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// 由于使用的是JWT,我们这里不需要csrf
.csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 允许对于网站静态资源的无授权访问
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
// 对于获取token的rest api要允许匿名访问
.antMatchers("/auth/**").permitAll()
.antMatchers("/admin/**").hasIpAddress("127.0.0.1")
.antMatchers("/admin/**").access("hasAuthority('ROLE_ADMIN')")
// .anyRequest().authenticated().and().formLogin().loginPage("/login")
// .failureUrl("/login?error").permitAll().and().logout().permitAll();
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
// 添加JWT filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// 禁用缓存
httpSecurity.headers().cacheControl();
}
}
到这边jwt整合也介绍了,现在我们就可以postman来进行测试。
测试结果如下所示:
生成token:
到这里关于 SpringBoot+SpringSecurity+JWT就基本结束了,这篇博客我重点是将如何通过代码来实现这个功能,关于代码本身没有很细致的介绍。关于代码功能的介绍,例如怎么通过注解配置权限控制,如何通过配置来进行权限控制,token更新和验证等等的细节,我会在我这篇博客的教学视频中介绍,视频地址:点击打开链接
GitHub地址:点击打开链接
如果大家对文章有什么问题或者疑意之类的,可以加我订阅号在上面留言,订阅号上面我会定期更新最新博客。如果嫌麻烦可以直接加我wechat:lzqcode