文章目录
- 1、微服务认证与授权实现思路
- 2、权限管理数据模型
- 3、项目结构和功能说明
- 3、核心业务
- 1. 代码结构图说明
- 2. 创建认证授权相关的工具类
- (1)DefaultPasswordEncoder:密码处理工具类
- (2)TokenManager:token操作的工具类
- (3)TokenLogoutHandler:退出实现
- (4)UnauthorizedEntryPoint:未授权统一处理
- 3. 创建认证授权实体类
-
- 4. 创建认证和授权的filter
- (1)TokenLoginFilter:认证的filter
- (2)TokenAuthenticationFilter:授权的filter
- 5. 创建核心配置类
- 6. 权限认证的实现类
- 4、整合前端后的效果图
1、微服务认证与授权实现思路
(1)如果是基于 Session,那么 Spring-security 会对 cookie 里的 sessionid 进行解析,找到服务器存储的 session 信息,然后判断当前用户是否符合请求的要求。
(2)如果是 token,则是解析出 token,然后将当前请求加入到 Spring-security 管理的权限信息中去。
如果系统的模块众多,每个模块都需要进行授权与认证,所以我们选择基于 token 的形式进行授权与认证,用户根据用户名密码认证成功,然后获取当前用户角色的一系列权限值,并以用户名为 key,权限列表为 value 的形式存入 redis 缓存中,根据用户名相关信息生成 token 返回,浏览器将 token 记录到 cookie 中,每次调用 api 接口都默认将 token 携带到 header 请求头中,Spring-security 解析 header 头获取 token 信息,解析 token 获取当前用户名,根据用户名就可以从 redis 中获取权限列表,这样 Spring-security 就能够判断当前请求是否有权限访问。
2、权限管理数据模型
3、项目结构和功能说明
项目主要实现的功能:
- 登录(认证)
- 添加角色
- 为角色分配权限
- 添加用户
- 为用户分配角色
项目涉及技术说明:
-
Maven
创建父工程:管理项目依赖版本
创建子模块:使用具体依赖
-
SpringBoot
本质就是Spring,是Spring快速构建工具
-
MyBatisPlus
操作数据库框架
-
SpringCloud
(1)GateWay网关:对外暴露一个统一的端口,通过网关转发到具体模块中去。
(2)Nacos注册中心:将所有模块都存放在注册中心去,方便服务之间的互相调用。
-
其他技术
Redis、Jwt、Swagger
-
前端技术
vue、elementUI
3、核心业务
1. 代码结构图说明
2. 创建认证授权相关的工具类
(1)DefaultPasswordEncoder:密码处理工具类
@Component
public class DefaultPasswordEncoder implements PasswordEncoder {
public DefaultPasswordEncoder() {
this(-1);
}
public DefaultPasswordEncoder(int strength) {
}
public String encode(CharSequence rawPassword) {
return MD5.encrypt(rawPassword.toString());
}
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(MD5.encrypt(rawPassword.toString()));
}
}
(2)TokenManager:token操作的工具类
使用jwt生成token
@Component
public class TokenManager {
private long tokenExpiration = 24*60*60*1000;
private String tokenSignKey = "123456";
public String createToken(String username) {
String token = Jwts.builder().setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
.signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact();
return token;
}
public String getUserFromToken(String token) {
String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject();
return user;
}
public void removeToken(String token) {
}
}
(3)TokenLogoutHandler:退出实现
public class TokenLogoutHandler implements LogoutHandler {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) {
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
@Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
String token = request.getHeader("token");
if (token != null) {
tokenManager.removeToken(token);
String userName = tokenManager.getUserFromToken(token);
redisTemplate.delete(userName);
}
ResponseUtil.out(response, R.ok());
}
}
(4)UnauthorizedEntryPoint:未授权统一处理
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
ResponseUtil.out(response, R.error());
}
}
3. 创建认证授权实体类
(1)User
@Data
@ApiModel(description = "用户实体类")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "微信openid")
private String username;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "昵称")
private String nickName;
@ApiModelProperty(value = "用户头像")
private String salt;
@ApiModelProperty(value = "用户签名")
private String token;
}
(2)SecutityUser
@Data
@Slf4j
public class SecurityUser implements UserDetails {
private transient User currentUserInfo;
private List<String> permissionValueList;
public SecurityUser() {
}
public SecurityUser(User user) {
if (user != null) {
this.currentUserInfo = user;
}
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
for(String permissionValue : permissionValueList) {
if(StringUtils.isEmpty(permissionValue)) continue;
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
authorities.add(authority);
}
return authorities;
}
@Override
public String getPassword() {
return currentUserInfo.getPassword();
}
@Override
public String getUsername() {
return currentUserInfo.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
4. 创建认证和授权的filter
(1)TokenLoginFilter:认证的filter
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
private AuthenticationManager authenticationManager;
public TokenLoginFilter(AuthenticationManager authenticationManager, TokenManager tokenManager, RedisTemplate redisTemplate) {
this.authenticationManager = authenticationManager;
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
this.setPostOnly(false);
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/admin/acl/login","POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res)
throws AuthenticationException {
try {
User user = new ObjectMapper().readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),
user.getPassword(), new ArrayList<>()));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,
Authentication auth) throws IOException, ServletException {
SecurityUser user = (SecurityUser) auth.getPrincipal();
String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());
ResponseUtil.out(res, R.ok().data("token", token));
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
ResponseUtil.out(response, R.error());
}
}
(2)TokenAuthenticationFilter:授权的filter
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
public TokenAuthenticationFilter(AuthenticationManager authManager, TokenManager tokenManager,RedisTemplate redisTemplate) {
super(authManager);
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
logger.info("================="+request.getRequestURI());
if(request.getRequestURI().indexOf("admin") == -1) {
chain.doFilter(request, response);
return;
}
UsernamePasswordAuthenticationToken authentication = null;
try {
authentication = getAuthentication(request);
} catch (Exception e) {
ResponseUtil.out(response, R.error());
}
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
ResponseUtil.out(response, R.error());
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader("token");
if (token != null && !"".equals(token.trim())) {
String userName = tokenManager.getUserFromToken(token);
List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(userName);
Collection<GrantedAuthority> authorities = new ArrayList<>();
for(String permissionValue : permissionValueList) {
if(StringUtils.isEmpty(permissionValue)) continue;
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
authorities.add(authority);
}
if (!StringUtils.isEmpty(userName)) {
return new UsernamePasswordAuthenticationToken(userName, token, authorities);
}
return null;
}
return null;
}
}
5. 创建核心配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private TokenManager tokenManager;
private DefaultPasswordEncoder defaultPasswordEncoder;
private RedisTemplate redisTemplate;
@Autowired
public TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,
TokenManager tokenManager, RedisTemplate redisTemplate) {
this.userDetailsService = userDetailsService;
this.defaultPasswordEncoder = defaultPasswordEncoder;
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new UnauthorizedEntryPoint())
.and().csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().logout().logoutUrl("/admin/acl/index/logout")
.addLogoutHandler(new TokenLogoutHandler(tokenManager,redisTemplate)).and()
.addFilter(new TokenLoginFilter(authenticationManager(), tokenManager, redisTemplate))
.addFilter(new TokenAuthenticationFilter(authenticationManager(), tokenManager, redisTemplate)).httpBasic();
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/**",
"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**");
}
}
6. 权限认证的实现类
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.selectByUsername(username);
if (null == user){
throw new UsernameNotFoundException("用户名不存在!");
}
com.kuang.serurity.entity.User curUser = new com.kuang.serurity.entity.User();
BeanUtils.copyProperties(user,curUser);
List<String> authorities = permissionService.selectPermissionValueByUserId(user.getId());
SecurityUser securityUser = new SecurityUser(curUser);
securityUser.setPermissionValueList(authorities);
return securityUser;
}
}
4、整合前端后的效果图
如果有收获!!! 希望老铁们来个三连,点赞、收藏、转发。
创作不易,别忘点个赞,可以让更多的人看到这篇文章,顺便鼓励我写出更好的博客
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)