上目录结构
//权限管理模块
// 1. 创建一个权限管理的子模块
//2. 引入相关依赖
// <dependencies>
//<!-- 自定义的模块 安全框架 security-->
// <dependency>
// <groupId>com.schoolWeb</groupId>
// <artifactId>spring_security</artifactId>
// <version>0.0.1-SNAPSHOT</version>
// </dependency>
// <dependency>
// <groupId>com.alibaba</groupId>
// <artifactId>fastjson</artifactId>
// </dependency>
// </dependencies>
//3. 创建一个 权限管理的模块 编写相对应的权限代码 代码比较固定 分享到git 上了
//主要是 菜单管理 角色管理 分配权限 等常用功能
//4. 创建一个 spring_security模块 放在了 common模块里了 他是公共的 代码比较固定 分享到git 上了
// Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。提供了完善的认证机制和方法级的授权功能
// 本质就是过滤器 filter
// 用于 用户认证 和用户授权 两部分
// 如果是基于Session 那么 Spring Security 会对cookie里的sessionid进行解析认证
// 如果是token 则会解析token 然后将当前请求加入到Spring Security 管理的权限信息里去
// 这个玩意 帮我们 取值 放值 帮我们 管理 token 或者 session 不需要我们 自己再去具体实现
// 如果系统的模块众多 每个模块都需要认证 用户根据用户密码进行认证 然后获取一系列的权限 并已用户名为key
// 权限为value的形式存入redis里 根据用户的header头部里的token, Spring Security 解析token 这样判断是否有请求权限
//5. 引入spring_security 的相关依赖
// <dependencies>
//<!-- 自定义的工具类-->
// <dependency>
// <groupId>com.schoolWeb</groupId>
// <artifactId>common_utils</artifactId>
// <version>0.0.1-SNAPSHOT</version>
// </dependency>
// <!-- Spring Security依赖 -->
// <dependency>
// <groupId>org.springframework.boot</groupId>
// <artifactId>spring-boot-starter-security</artifactId>
// </dependency>
//
// <dependency>
// <groupId>io.jsonwebtoken</groupId>
// <artifactId>jjwt</artifactId>
// </dependency>
// </dependencies>
//6. 整合 spring security 框架 具体实现类
//7. 需要一个md5 和ResponseUtil 工具类 md5自行查找
public class ResponseUtil {
public static void out(HttpServletResponse response, R r) {
ObjectMapper mapper = new ObjectMapper();
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
try {
mapper.writeValue(response.getWriter(), r);
} catch (IOException e) {
e.printStackTrace();
}
}
}
//8. 需要配置核心配置类的退出地址 TokenWebSecurityConfig.java
@Configuration
@EnableWebSecurity //开启权限控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService; // 自定义查询数据库 认证密码
private TokenManager tokenManager; //token 工具
private DefaultPasswordEncoder defaultPasswordEncoder; // 密码处理
private RedisTemplate redisTemplate; // redis 处理
@Autowired
public TokenWebSecurityConfig(UserDetailsService userDetailsService, DefaultPasswordEncoder defaultPasswordEncoder,
TokenManager tokenManager, RedisTemplate redisTemplate) {
this.userDetailsService = userDetailsService;
this.defaultPasswordEncoder = defaultPasswordEncoder;
this.tokenManager = tokenManager;
this.redisTemplate = redisTemplate;
}
/**
* 配置设置
* @param http
* @throws Exception
*/
@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();
}
/**
* 配置哪些请求不拦截 这个注意一下 要不 登录退出路径 有可能报错 404
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/**",
"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"
);
// web.ignoring().antMatchers("/*/**"
// );
}
/**
* 密码处理
* @param auth
* @throws Exception
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(defaultPasswordEncoder);
}
//9. 实现 security提供的UserDetails 接口 SecurityUser.java
package com.school.security.entity;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* <p>
* 安全认证用户详情信息
* </p>
* 相关实体类
* @author qy
* @since 2019-11-08
*/
@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;
}
}
//10. 登录校验认证过滤器 配置登录地址 TokenLoginFilter.java
/**
* <p>
* 登录过滤器,继承UsernamePasswordAuthenticationFilter,对用户名密码进行登录校验
* </p>
* 认证过滤器
* @author qy
* @since 2019-11-08
*/
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
private TokenManager tokenManager;
private RedisTemplate redisTemplate;
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);
}
}
/**
* 登录成功 执行这个
* @param req
* @param res
* @param chain
* @param auth
* @throws IOException
* @throws ServletException
*/
@Override
protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain,
Authentication auth) throws IOException, ServletException {
SecurityUser user = (SecurityUser) auth.getPrincipal();
//生成token 值
String token = tokenManager.createToken(user.getCurrentUserInfo().getUsername());
//根据用户名 把需要的 权限 放到 redis里
redisTemplate.opsForValue().set(user.getCurrentUserInfo().getUsername(), user.getPermissionValueList());
ResponseUtil.out(res, R.ok().data("token", token));
}
/**
* 登录失败 执行这个
* @param request
* @param response
* @param e
* @throws IOException
* @throws ServletException
*/
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
ResponseUtil.out(response, R.error());
}
}
//11. 授权过滤器
//12. 配置好 spring security 框架后 把这个 模块引入到某个的模块下( 就是步骤2 那个依赖)
// 比如 你 创建的 权限管理模块(acl) 这个模块下 都是权限授权和配置的操作
// 就可以引入到这个模块下 在这个模块具体使用spring security 下的功能
//13. 需要创建一个查询登录和用户权限的实现类 这个类 需要实现 spring security 下的 UserDetailsService 的接口
// 具体实现查询验证 用户登录 得到数据
// 注: 使用这种框架 思想为 :面向接口发开 或者 面向 spring security 开发
// 帮我们 实现了 验证 和授权登录 我们只需要 修改配置文件和 查询数据库数据 验证成功就行了
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
/***
* 根据账号获取用户信息
* @param username:
* @return: org.springframework.security.core.userdetails.UserDetails
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从数据库中取出用户信息
User user = userService.selectByUsername(username);
// 判断用户是否存在
if (null == user){
//throw new UsernameNotFoundException("用户名不存在!");
}
// 返回UserDetails实现类
com.school.security.entity.User curUser = new com.school.security.entity.User();
BeanUtils.copyProperties(user,curUser);
//获取权限
List<String> authorities = permissionService.selectPermissionValueByUserId(user.getId());
SecurityUser securityUser = new SecurityUser(curUser);
securityUser.setPermissionValueList(authorities);
return securityUser;
}
}
// 使用了这个security 框架 实现登录后 自己开发的 登录 就无需再用了
github地址
javaUtils/spring_security at main · Json870422471/javaUtils · GitHub