首先呢就是需求:

1、账号、密码进行第一次登录,获得token,之后的每次请求都在请求头里加上这个token就不用带账号、密码或是session了。

2、用户有两种类型,具体表现在数据库中存用户信息时是分开两张表进行存储的。

为什么会分开存两张表呢,这个设计的时候是先设计的表结构,有分开的必要所以就分开存了,也没有想过之后Security 这块需要进行一些修改,但是分开存就分开存吧,Security 这块也不是很复杂。

maven就是这两:

        <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>

然后直接说代码吧,首先呢是实现dao层,这一层就不贴代码了,反正就是能根据用户名能返回用户信息就好了。

所以其实第一步是实现自己的安全模型:

这一步是实现UserDetails这个接口,其中我额外添加了用户类型、用户Id。其他的都是UserDetails接口必须实现的。

/**
* 安全用户模型
* @author xuwang
* Created on 2019/05/28 20:07
*/
public class XWUserDetails implements UserDetails {
//用户类型code
public final static String USER_TYPE_CODE = "1";
//管理员类型code
public final static String MANAGER_TYPE_CODE = "2";
//用户id
private Integer userId;
//用户名
private String username;
//密码
private String password;
//用户类型
private String userType;
//用户角色表
private Collection<? extends GrantedAuthority> authorities; public XWUserDetails(Integer userId,String username, String password, String userType, Collection<? extends GrantedAuthority> authorities){
this.userId = userId;
this.username = username;
this.password = password;
this.userType = userType;
this.authorities = authorities;
} /**
* 获取权限列表
* @return Collection
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
} /**
* 获取用户Id
* @return String
*/
public Integer getUserId() {
return userId;
} /**
* 获取用户类型
* @return String
*/
public String getUserType() {
return userType;
} /**
* 获取密码
* @return String
*/
@Override
public String getPassword() {
return password;
} /**
* 获取用户名
* @return String
*/
@Override
public String getUsername() {
return username;
} /**
* 账号是否未过期
* @return boolean
*/
@Override
public boolean isAccountNonExpired() {
return true;
} /**
* 账号是否未锁定
* @return boolean
*/
@Override
public boolean isAccountNonLocked() {
return true;
} /**
* 凭证是否未过期
* @return boolean
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
} /**
* 账号是否已启用
* @return boolean
*/
@Override
public boolean isEnabled() {
return true;
}

第二步是实现两个UserDetailsService因为要从两张表里进行查询,所以我就实现了两个UserDetailsService

这一步呢,注入了dao层的东西,从数据库中查询出用户信息,构建XWUserDetails并返回。

/**
* Manager专用的UserDetailsService
* @author xuwang
* Created on 2019/06/01 15:58
*/
@Service("managerDetailsService")
public class ManagerDetailsServiceImpl implements UserDetailsService {
@Resource
ScManagerMapper_Security scManagerMapper_security;
@Resource
ScRoleMapper_Security scRole_Mapper_security; /**
* 根据用户名从数据库中获取XWUserDetails
* @param username
* @return UserDetails
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//获取用户信息
ScManager user = scManagerMapper_security.findByUsername(username);
//获取角色列表
List<String> roles = scRole_Mapper_security.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
} else {
return new XWUserDetails(user.getId(),user.getManagerName(), user.getLoginPass(),XWUserDetails.MANAGER_TYPE_CODE, roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
}
}
}
/**
* User专用的UserDetailsService
* @author xuwang
* Created on 2019/06/01 15:58
*/
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource
ScUserMapper_Security userMapper_security;
@Resource
ScRoleMapper_Security scRole_Mapper_security;
/**
* 根据用户名从数据库中获取XWUserDetails
* @param username
* @return UserDetails
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//获取用户信息
ScUser user = userMapper_security.findByUsername(username);
//获取角色列表
List<String> roles = scRole_Mapper_security.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
} else {
return new XWUserDetails(user.getId(),user.getName(),user.getPassword(), XWUserDetails.MANAGER_TYPE_CODE, roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
}
} }

第三步,实现两个UsernamePasswordAuthenticationToken

这一步的话,其实单看不知道为什么实现两个类,但是注释里面我写了,然后真正的为什么,整体流程,到最后说吧。

/**
* manager专用的UsernamePasswordAuthenticationToken
* AuthenticationManager会遍历使用Provider的supports()方法,判断AuthenticationToken是不是自己想要的
* @author xuwang
* Created on 2019/06/01 15:58
*/
public class ManagerAuthenticationToken extends UsernamePasswordAuthenticationToken {
public ManagerAuthenticationToken(Object principal, Object credentials) {
super(principal, credentials);
}
}
/**
* User专用的UsernamePasswordAuthenticationToken
* AuthenticationManager会遍历使用Provider的supports()方法,判断AuthenticationToken是不是自己想要的
* @author xuwang
* Created on 2019/06/01 15:58
*/
public class UserAuthenticationToken extends UsernamePasswordAuthenticationToken {
public UserAuthenticationToken(Object principal, Object credentials){
super(principal,credentials);
}
}

第四步,实现两个AuthenticationProvider

这个地方用到了上面的两个类,重点是supports()方法,这个方法是用来校验传进来的UsernamePasswordAuthenticationToken的,反正就代表着这个ManagerAuthenticationProvider就只适用于ManagerAuthenticationToken,另一个同理,具体也是最后说吧。

/**
* Manager专用的AuthenticationProvider
* 选择实现DaoAuthenticationProvider是因为比较方便且能用
* @author xuwang
* Created on 2019/06/01 15:58
*/
public class ManagerAuthenticationProvider extends DaoAuthenticationProvider {
/**
* 初始化 将使用Manager专用的userDetailsService
* @param encoder
* @param userDetailsService
*/
public ManagerAuthenticationProvider(PasswordEncoder encoder, UserDetailsService userDetailsService){
setPasswordEncoder(encoder);
setUserDetailsService(userDetailsService);
} @Override
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
super.setPasswordEncoder(passwordEncoder);
} @Override
public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
super.setUserDetailsPasswordService(userDetailsPasswordService);
} /**
* 判断只有传入ManagerAuthenticationToken的时候才使用这个Provider
* supports会在AuthenticationManager层被调用
* @param authentication
* @return
*/
public boolean supports(Class<?> authentication) {
return ManagerAuthenticationToken.class.isAssignableFrom(authentication);
}
}
/**
* 实现User专用的AuthenticationProvider
* 选择实现DaoAuthenticationProvider是因为比较方便且能用
* @author xuwang
* Created on 2019/06/01 15:58
*/
public class UserAuthenticationProvider extends DaoAuthenticationProvider { /**
* 初始化 将使用User专用的userDetailsService
* @param encoder
* @param userDetailsService
*/
public UserAuthenticationProvider(PasswordEncoder encoder, UserDetailsService userDetailsService){
setPasswordEncoder(encoder);
setUserDetailsService(userDetailsService);
} @Override
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
super.setPasswordEncoder(passwordEncoder);
} @Override
public void setUserDetailsPasswordService(UserDetailsPasswordService userDetailsPasswordService) {
super.setUserDetailsPasswordService(userDetailsPasswordService);
} /**
* 判断只有传入UserAuthenticationToken的时候才使用这个Provider
* supports会在AuthenticationManager层被调用
* @param authentication
* @return
*/
public boolean supports(Class<?> authentication) {
return UserAuthenticationToken.class.isAssignableFrom(authentication);
}
}

第五步就是继承实现这个WebSecurityConfigurerAdapter

这一步呢,主要是将上面两个AuthenticationProvider加入到AuthenticationManager中,并向Spring中注入这个AuthenticationManager供Service在校验账号密码时使用。

同时还注入了一个PasswordEncoder,也是同样供Service层使用,反正就是其他地方能用就是了,就不用new了。

然后是configure方法,这个里面,具体就是Security 的配置了,为什么怎么写我就不说了,反正我这里实现了url的配置、Session的关闭、Filter的设置、设置验证失败权限不足自定义返回值。

其中Filter、和验证失败权限不足再看后面的代码吧,我也会贴上的。

关于AuthenticationManager,就是先用加密工具、和之前实现的UserDetailsService 构造两个DaoAuthenticationProvider,然后在configureGlobal()方法中添加这两个DaoAuthenticationProvider,最后authenticationManagerBean()方法进行注入。

/**
* Security 配置
* @author xuwang
* Created on 2019/06/01 15:58
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class XWSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("managerDetailsService")
private UserDetailsService managerDetailsService;
@Resource
private XWAuthenticationTokenFilter xwAuthenticationTokenFilter;
@Resource
private EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;
@Resource
private RestAccessDeniedHandler restAccessDeniedHandler; /**
* 注入UserAuthenticationProvider
* @return
*/
@Bean("UserAuthenticationProvider")
DaoAuthenticationProvider daoUserAuthenticationProvider(){
return new UserAuthenticationProvider(encoder(), userDetailsService);
} /**
* 注入ManagerAuthenticationProvider
* @return
*/
@Bean("ManagerAuthenticationProvider")
DaoAuthenticationProvider daoMangerAuthenticationProvider(){
return new ManagerAuthenticationProvider(encoder(), managerDetailsService);
} /**
* 向AuthenticationManager添加Provider
* @return
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth){
auth.authenticationProvider(daoUserAuthenticationProvider());
auth.authenticationProvider(daoMangerAuthenticationProvider());
} @Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder);
} /**
* 注入AuthenticationManager
* @return
*/
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
} /**
* 注入PasswordEncoder
* @return
*/
@Bean
public PasswordEncoder encoder() {
PasswordEncoder encoder =
PasswordEncoderFactories.createDelegatingPasswordEncoder();
return encoder;
} /**
* 具体Security 配置
* @return
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
csrf().disable().//默认开启,这里先显式关闭csrf
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) //Spring Security永远不会创建HttpSession,它不会使用HttpSession来获取SecurityContext
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() //任何用户任意方法可以访问/**
.antMatchers("/base/login").permitAll() //任何用户可以访问/user/**
.anyRequest().authenticated() //任何没有匹配上的其他的url请求,只需要用户被验证
.and()
.headers().cacheControl();
http.addFilterBefore(xwAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().authenticationEntryPoint(entryPointUnauthorizedHandler).accessDeniedHandler(restAccessDeniedHandler);
} }

然后是上面的两个Handler

这个很简单,就是实现AccessDeniedHandler和AuthenticationEntryPoint就是了。

/**
* 身份验证失败自定401返回值
*
* @author xuwang
* Created on 2019/05/29 16:10.
*/
@Component
public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.setStatus(401);
}
}
/**
* 权限不足自定403返回值
*
* @author xuwang
* Created on 2019/05/29 16:10.
*/
@Component
public class RestAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
httpServletResponse.setStatus(403);
}
}

再然后就是JWT这一块了。

首先是一个Token的工具类,里面有些什么东西直接看注释就好了。

/**
* JWT工具类
*
* @author xuwang
* Created on 2019/05/28 20:16.
*/
@Component
public class XWTokenUtil implements Serializable { /**
* 密钥
*/
private final String secret = "11111111"; /**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String generateToken(Map<String, Object> claims) {
//有效时间
Date expirationDate = new Date(System.currentTimeMillis() + 2592000L * 1000);
return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
} /**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
claims = null;
}
return claims;
} /**
* 生成令牌
*
* @param userDetails 用户
* @return 令牌
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>(2);
claims.put("sub", userDetails.getUsername());
claims.put("userId", ((XWUserDetails)userDetails).getUserId());
claims.put("userType", ((XWUserDetails)userDetails).getUserType());
claims.put("created", new Date());
return generateToken(claims);
} /**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
public String getUsernameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
} /**
* 从令牌中获取用户类型
*
* @param token 令牌
* @return 用户类型
*/
public String getUserTypeFromToken(String token) {
String userType;
try {
Claims claims = getClaimsFromToken(token);
userType = (String) claims.get("userType");
} catch (Exception e) {
userType = null;
}
return userType;
} /**
* 从令牌中获取用户Id
*
* @param token 令牌
* @return 用户Id
*/
public Integer getUserIdFromToken(String token) {
Integer userId;
try {
Claims claims = getClaimsFromToken(token);
userId = (Integer) claims.get("userId");
} catch (Exception e) {
userId = null;
}
return userId;
} /**
* 判断令牌是否过期
*
* @param token 令牌
* @return 是否过期
*/
public Boolean isTokenExpired(String token) {
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
return false;
}
} /**
* 刷新令牌
*
* @param token 原令牌
* @return 新令牌
*/
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put("created", new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
} /**
* 验证令牌
*
* @param token 令牌
* @param userDetails 用户
* @return 是否有效
*/
public Boolean validateToken(String token, UserDetails userDetails) {
XWUserDetails user = (xwUserDetails) userDetails;
String username = getUsernameFromToken(token);
return (username.equals(user.getUsername()) && !isTokenExpired(token));
}
}

然后是Filter

这个Filter大家都知道请求发过来,会先进行这个Filter里面的方法,这里的逻辑也很简单,从Token中拿到身份信息,并进行验证,验证这里我写得比简单,可以再加逻辑。

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());

authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SecurityContextHolder.getContext().setAuthentication(authentication);

重点是这三行,这三行是什么意思呢,前面说了需求是第一次登录验证成功,以后发请求使用Token就好了,这三行之前的逻辑是在校验Token,从Token中获取用户信息,但系统中进行权限管理的是Spring Security,并没有使用Spring Security 进行验证啊,

所以需要做的就是这三行,这三行中的SecurityContextHolder就是:SecurityContextHolder是用来保存SecurityContext的。SecurityContext中含有当前正在访问系统的用户的详细信息,

实际就是使用用户信息构建authentication放到SecurityContextHolder就等于用户已经登录了,就不用再校验密码什么的了。

/**
* JWT Filter
*
* @author xuwang
* Created on 2019/05/29 16:10.
*/
@Component
public class XWAuthenticationTokenFilter extends OncePerRequestFilter { @Resource
ManagerDetailsServiceImpl managerDetailsService;
@Resource
UserDetailsServiceImpl userDetailsService;
@Resource
private XWTokenUtil xwTokenUtil; /**
* 获取验证token中的身份信息
* @author xuwang
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
//从请求头中获取token
String authHeader = request.getHeader("Authorization");
//token前缀
String tokenHead = "Bearer ";
if (authHeader != null && authHeader.startsWith(tokenHead)) {
//去掉token前缀
String authToken = authHeader.substring(tokenHead.length());
//从token中获取用户名
String username = XWTokenUtil.getUsernameFromToken(authToken);
String userType = XWTokenUtil.getUserTypeFromToken(authToken);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = null;
//根据从token中获取用户名从数据库中获取一个userDetails
if(userType.equals(XWUserDetails.USER_TYPE_CODE)){
//普通用户
userDetails = userDetailsService.loadUserByUsername(username);
}else if(userType.equals(XWUserDetails.MANAGER_TYPE_CODE)){
//管理员
userDetails = managerDetailsService.loadUserByUsername(username);
}
if (xwTokenUtil.validateToken(authToken, userDetails)) {
//token中的用户信息和数据库中的用户信息对比成功后将用户信息加入SecurityContextHolder相当于登陆
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
} }

然后是用户登录的接口了,我直接贴Service层的代码吧

/**
* @ClassName: loginServiceImpl
* @ClassNameExplain:
* @Description: 业务层实现类
* @author xuwang
* @date 2019-05-31 16:15:46
*/
@Service
public class LoginServiceImpl implements ILoginService { static final Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class); @Resource
private AuthenticationManager authenticationManager;
@Autowired
@Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
@Autowired
@Qualifier("managerDetailsService")
private UserDetailsService managerDetailsService;
@Resource
private XWTokenUtil xwTokenUtil; @Override
public LoginVO login(LoginIO loginIO) throws Exception {
//不同的用户类型使用不同的登陆方式
String token = "";
UserDetails userDetails = null;
if(loginIO.getType().equals(XWUserDetails.USER_TYPE_CODE)){
//登录
login(new UserAuthenticationToken(loginIO.getUserName(), loginIO.getPassword()));
userDetails = userDetailsService.loadUserByUsername(loginIO.getUserName());
token = xwTokenUtil.generateToken(userDetails);
logger.info("user[{}]登陆成功",loginIO.getUserName());
}else if(loginIO.getType().equals(XWUserDetails.MANAGER_TYPE_CODE)){
login(new ManagerAuthenticationToken(loginIO.getUserName(), loginIO.getPassword()));
userDetails = managerDetailsService.loadUserByUsername(loginIO.getUserName());
token = xwUtil.generateToken(userDetails);
logger.info("manager[{}]登陆成功",loginIO.getUserName());
}else {
logger.error("type[{}]参数错误",loginIO.getType());
//type参数错误
throw new BusinessException(ExceptionConstants.PARAM_INVALID_CODE,
ExceptionConstants.PARAM_INVALID_MSG);
}
LoginVO loginVO = new LoginVO();
loginVO.setToken(token);
loginVO.setUserId(((XWUserDetails)userDetails).getUserId());
return loginVO == null ? new LoginVO() : loginVO;
} /**
* 校验账号密码并进行登陆
* @param upToken
*/
private void login(UsernamePasswordAuthenticationToken upToken){
//验证
Authentication authentication = authenticationManager.authenticate(upToken);
//将用户信息保存到SecurityContextHolder=登陆
SecurityContextHolder.getContext().setAuthentication(authentication);
} }

这个Service解释一下就是:

loginIO能接收到用户信息:账号UserName、密码Password、类型Type之类的,然后使用AuthenticationManager 进行校验,再使用SecurityContextHolder进行登录操作(上面解释过了),最后返回Token(xwTokenUtil工具类生成的)和用户Id。

最后解释一下其中的流程,已经我为什么这么去实现吧。

从Service中我们可以看到,登录时使用的是先使用账号密码构建了一个UsernamePasswordAuthenticationToken,我这里构建的是UserAuthenticationToken、ManagerAuthenticationToken,不过影响不大,都是它的子类,AuthenticationManager的authenticate将接受一个UsernamePasswordAuthenticationToken来进行验证,最后才登录。

上面的相当于Security的登录使用流程。

然后解释一下前面的那些所有的疑惑,在Service中使用AuthenticationManager的authenticate()方法进行校验的时候,实际上是会把UsernamePasswordAuthenticationToken传递给Provider进行校验的,Provider里呢又让Service去校验的。这是类和类之间的关系,然后还有实际代码关系是,AuthenticationManager中会有一个Provider列表,进行校验的时候会遍历使用每一个Provider的supports()方法,这个supports()方法将校验传进来的UsernamePasswordAuthenticationToken是自己想要的UsernamePasswordAuthenticationToken吗,如果是的话就使用这个Provider进行校验。所以我实现了UserAuthenticationToken、ManagerAuthenticationToken,还实现了ManagerAuthenticationProvider、UserAuthenticationProvider以及其中的supports()方法,这样authenticationManager.authenticate(new UserAuthenticationToken)就会使用UserAuthenticationProvider中的UserDetailsServiceImpl去校验了。ManagerAuthenticationToken同理。

上面的我自己是觉得写得是比较清晰了。如果实在是看不明白,或者其实是我还是写得太烂了,可以自己跟一下代码,就从AuthenticationManager.authenticate()方法跟进去就好了,

具体跟代码的时候要注意,AuthenticationManager的默认实现是ProviderManager,所以其实看到的是ProviderManager的authenticate()方法

图中:

  1. 获取到Authentication的类信息

  2. 得到Provider列表的迭代器

  3.进行遍历

  4.调用Provider的supports()方法

所以我重写了两个provider和其中supports()方法,和两个AuthenticationToken。

然后其实这个东西并不算是太复杂,自己去用和学习的时候,最好还是先实现,然后在慢慢跟代码,去猜去思考其中的流程,就好了。

最后是为什么要这样去写代码、去注入、用这个方式进行加密、以及token中存放的信息、loginIo得设置等等的,这些都是可以任意更改的,无需纠结太多,根据根据个人习惯和当时的业务改就好了,至于到底怎样才是最好的,我也没太认真的去思考过,毕竟加班的时候只能先实现功能了,至于为什么在写这个博客的时候还不去思考的原因就是。。。因为这些并不是重点

Spring Boot + Security + JWT 实现Token验证+多Provider——登录系统的更多相关文章

  1. 实战!spring Boot security+JWT 前后端分离架构认证登录!

    大家好,我是不才陈某~ 认证.授权是实战项目中必不可少的部分,而Spring Security则将作为首选安全组件,因此陈某新开了 <Spring Security 进阶> 这个专栏,写一 ...

  2. Spring Boot Security JWT 整合实现前后端分离认证示例

    前面两章节我们介绍了 Spring Boot Security 快速入门 和 Spring Boot JWT 快速入门,本章节使用 JWT 和 Spring Boot Security 构件一个前后端 ...

  3. Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器

    概要 之前的两篇文章,讲述了Spring Security 结合 OAuth2 .JWT 的使用,这一节要求对 OAuth2.JWT 有了解,若不清楚,先移步到下面两篇提前了解下. Spring Bo ...

  4. Spring Boot Security 整合 JWT 实现 无状态的分布式API接口

    简介 JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案.JSON Web Token 入门教程 - 阮一峰,这篇文章可以帮你了解JWT的概念.本文重点讲解Spring Boo ...

  5. Spring Boot Security And JSON Web Token

    Spring Boot Security And JSON Web Token 说明 流程说明 何时生成和使用jwt,其实我们主要是token更有意义并携带一些信息 https://github.co ...

  6. Spring Boot Security 整合 OAuth2 设计安全API接口服务

    简介 OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版.本文重点讲解Spring Boot项目对OAuth2进行的实现,如果你对OAut ...

  7. Spring Boot初识(4)- Spring Boot整合JWT

    一.本文介绍 上篇文章讲到Spring Boot整合Swagger的时候其实我就在思考关于接口安全的问题了,在这篇文章了我整合了JWT用来保证接口的安全性.我会先简单介绍一下JWT然后在上篇文章的基础 ...

  8. Spring Boot Security配置教程

    1.简介 在本文中,我们将了解Spring Boot对spring Security的支持. 简而言之,我们将专注于默认Security配置以及如何在需要时禁用或自定义它. 2.默认Security设 ...

  9. Spring Boot Security Oauth2之客户端模式及密码模式实现

    Spring Boot Security Oauth2之客户端模式及密码模式实现 示例主要内容 1.多认证模式(密码模式.客户端模式) 2.token存到redis支持 3.资源保护 4.密码模式用户 ...

随机推荐

  1. svn unable to connect to a repository url 计算机积极拒绝

    网上应该说启动server服务,首先找不到这个服务,后来下载个软件有了,启动还是不行.clear了所有saved data之后也是不行. 解决方法:Network中 Enable proxy Serv ...

  2. scrapy基础知识之 Scrapy-Redis分布式策略:

    Scrapy-Redis分布式策略: 假设有四台电脑:Windows 10.Mac OS X.Ubuntu 16.04.CentOS 7.2,任意一台电脑都可以作为 Master端 或 Slaver端 ...

  3. 寻觅Azure上的Athena和BigQuery (二):神奇的PolyBase

    前情回顾 在“数据湖”概念与理论逐渐深入人心的今天,面向云存储的交互式查询这个需求场景显得愈发重要.这是因为原生的云存储(主要指S3这样的对象存储)既能够容纳大容量的明细数据,又能在性能和成本间取得一 ...

  4. 1. Python 魔法方法

    Python 魔法方法 基础: 如果你想... 所以,你写... Python调用... 初始化一个实例 x = MyClass() x.__init__() 作为一个字符串的"官方&quo ...

  5. MYSQL语句强化练习

    之前发现自己写sql不怎么得心应手,总是百度零零散散的学习一下,所以在空闲的时候自己就专门找一下mysql的强化题敲一下练习一下,简要记录一下,sql写着写着就会越来越熟练,总之要自己加油! 表结构 ...

  6. ~~函数基础(七):生成器&迭代器~~

    进击のpython 生成器 上来说个这,就有点抽象了! 我们先整点活儿 宁,准备好了吗? 直接相位猛冲! 列表生成器 需求来了,老弟!我有一个数组 a = [1, 2, 3, 4, 5, 6, 7, ...

  7. printf打印参数的顺序问题

    C语言的printf函数处理的参数顺序是从右向左的,例如如下程序: #include <stdio.h>    int main()  {      int a = 1, b = 2, c ...

  8. 【基础算法模拟+例题】-C++

    在漫长的刷题练习过程中,几乎所有稍微熟练一点的OIer都会,但是都几乎没有经过系统的学习,今天,我们就来讲讲模拟算法,也是为了复习emm. 定义? 定义?模拟还有什么定义吗? 那什么是模拟呢? 就是按 ...

  9. Excel催化剂开源第1波-自定义函数的源代码全公开

    Excel催化剂插件从2018年1月1日开始运营,到今天刚好一周年,在过去一年时间里,感谢社区里的许多友人们的关心和鼓励,得以坚持下来,并收获一定的用户量和粉丝数和少量的经济收入回报和个人知名度的提升 ...

  10. .NET Core 3.0之深入源码理解HttpClientFactory(一)

    写在前面 创建HttpClient实例的时候,在内部会创建HttpMessageHandler链,我们知道HttpMessageHandler是负责建立连接的抽象处理程序,所以HttpClient的维 ...