第五次课:后台整合springsecurity
分类: springboot 专栏: 在线教育项目实战 标签: security整合
2023-05-08 12:26:41 1177浏览
security整合
依赖
<!--JWT 依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<!--spring security依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
UrlSecurityMetadataSource urlSecurityMetadataSource;
@Resource
UserAccessDecisionManager userAccessDecisionManager;
@Resource
RestAuthenticationEntryPoint restAuthorizationEntryPoint;
@Resource
RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Resource
EduAclUserServiceImpl userService;
//直接不放弃md5加盐加密 改成
@Bean
BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 认证
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
/**
* 放行白名单
* @param web
* @throws Exception
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/user/login",
"/logout",
"/css/**",
"/js/**",
"/index.html",
"favicon.ico",
"/doc.html",
"/webjars/**",
"/swagger-resources/**",
"/v2/api-docs/**",
"/captcha",
"/ws/**"
);
}
/**
* 授权
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//使用JWT,不需要csrf
http.csrf().disable()
//基于token,不需要session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//所有请求都要求认证
.and()
//支持跨域访问
.cors()
.and()
.authorizeRequests()
// 放行OPTIONS请求
// .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest()
.authenticated()
//动态权限配置
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O object) {
//查找访问当前url需要什么角色(权限)
object.setSecurityMetadataSource(urlSecurityMetadataSource);
//判断(裁决)当前用户有没有这个角色(权限)
object.setAccessDecisionManager(userAccessDecisionManager);
return object;
}
})
.and()
//禁用缓存
.headers()
.cacheControl();
//添加jwt 登录授权过滤器
http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//添加自定义未授权和未登录结果返回
http.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthorizationEntryPoint);
}
@Bean
public JwtAuthencationTokenFilter jwtAuthencationTokenFilter(){
return new JwtAuthencationTokenFilter();
}
}用户实体类改造
public class EduAclUser implements Serializable , UserDetails
@TableField(exist = false)
private List<EduAclRole> roles;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>();
for (EduAclRole role : roles) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getRoleCode());
authorities.add(authority);
}
return authorities;
}用户service改造
@Service
public class EduAclUserServiceImpl extends ServiceImpl<EduAclUserMapper, EduAclUser>
implements EduAclUserService, UserDetailsService {
@Resource
EduAclUserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查询的时候一定要把密码和角色权限带上(所以要写sql映射文件)
EduAclUser eduAclUser = userMapper.getByUsername(username);
if (eduAclUser == null ){
throw new UsernameNotFoundException("用户不存在");
}
return eduAclUser;
}菜单实体改造
@TableField(exist = false) private List<EduAclRole> roles;
菜单service改造
@Override
public List<EduAclPermission> getAll() {
//这个要写sql映射文件,查菜单的时候把角色带回来
return permissionMapper.getAll();
}登录controller
@RestController
@CrossOrigin
public class LoginController {
@Resource
EduAclUserServiceImpl eduAclUserService;
@Resource
private PasswordEncoder passwordEncoder;
@Resource
private JwtTokenUtil jwtTokenUtil;
@PostMapping("/user/login")
public ResultDto login(@RequestBody AclUserVo userVo){
Map<String,String> resultMap = new HashMap<>();
String username = userVo.getUsername();
String password = userVo.getPassword();
UserDetails user = eduAclUserService.loadUserByUsername(username);
if (null==user||!passwordEncoder.matches(password,user.getPassword())){
return ResultDto.error("用户名或密码不正确");
}
if (!user.isEnabled()){
return ResultDto.error("账号被禁用,请联系管理员!");
}
/**
* 用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保存在会话中。
* spring security 提供会话管理,
* 认证通过后将身份信息放入SecurityContextHolder上下文
*/
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password,user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);
//生成token
String tokenStr = jwtTokenUtil.getToken(username);
resultMap.put("token",tokenStr);
return ResultDto.success("登录成功",resultMap);
}
}
查找访问当前url需要什么角色
@Component
public class UrlSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Resource
EduAclPermissionService permissionService;
AntPathMatcher matcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
String requestUrl = ((FilterInvocation) object).getRequestUrl();
List<EduAclPermission> allMenus = permissionService.getAll();
for (EduAclPermission menu : allMenus) {
if(matcher.match(menu.getPermissionValue(),requestUrl)){
List<EduAclRole> roles = menu.getRoles();
String[] roleCodes = new String[roles.size()];
for (int i = 0; i < roles.size(); i++) {
roleCodes[i] = roles.get(i).getRoleCode();
}
return SecurityConfig.createList(roleCodes);
}
}
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return false;
}裁决当前用户有没有这个角色
@Component
public class UserAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
////判断用户角色是否为url所需角色
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (ConfigAttribute configAttribute : configAttributes) {
//当前url所需角色
String needRole = configAttribute.getAttribute();
//判断角色是否登录即可访问的角色,此角色在CustomFilter中设置
if ("ROLE_LOGIN".equals(needRole)){
//判断是否登录
if (authentication instanceof AnonymousAuthenticationToken){
throw new AccessDeniedException("尚未登录,请登录!");
}else {
return;
}
}
for (GrantedAuthority authority : authorities) {
if(needRole.equals(authority.getAuthority())){
return;
}
}
}
throw new AccessDeniedException("权限不足");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return false;
}
@Override
public boolean supports(Class<?> clazz) {
return false;
}
}
登录授权过滤器
public class JwtAuthencationTokenFilter extends OncePerRequestFilter {
@Autowired
private EduAclUserServiceImpl userInfoServie;
@Autowired
JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authToken = request.getHeader(tokenHeader);
//存在token
if (null != authToken && authToken.startsWith(tokenHead)) {
String token = authToken.substring(tokenHead.length());
String username = jwtTokenUtil.getUserNameFromToken(token);
UserDetails userDetails = userInfoServie.loadUserByUsername(username);
//验证token是否有效,重新设置用户对象
if (jwtTokenUtil.validateToken(token)) {
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(request, response);
}
}退出登录
@ApiOperation(value = "退出登录")
@PostMapping("/logout")
public ResultDto logout(HttpServletRequest request, HttpServletResponse response){
//将项目中的用户信息清空
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication !=null){
new SecurityContextLogoutHandler().logout(request,response,authentication);
}
response.setContentType("application/json;chartset=utf-8");
//请求头里的jwt令牌置空
response.setHeader("Authorization","");
return ResultDto.success("注销成功",null);
}jwt配置
#jwt存储的请求头 jwt.tokenHeader=Authorization #jwt加密使用的密钥 jwt.secret=jfit-secret #jwt的过期时间(60*60*24) jwt.expiration=604800 #jwt载荷中拿到开头 jwt.tokenHead=Bearer
jwt工具类
@Component
public class JwtTokenUtil {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String getToken(String username){
Map<String,Object> map = new HashMap<>();
map.put("typ","JWT");
return Jwts.builder().setHeaderParams(map)
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis()+expiration))
.signWith(SignatureAlgorithm.HS256,secret).compact();
}
/**
* 校验token的合法性
* @return
*/
public Boolean validateToken(String token ){
try {
Claims body = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
return true;
} catch (ExpiredJwtException e) {
e.printStackTrace();
throw new JfException(50000,"token过期");
} catch (UnsupportedJwtException e) {
e.printStackTrace();
return false;
} catch (MalformedJwtException e) {
e.printStackTrace();
return false;
} catch (SignatureException e) {
e.printStackTrace();
throw new JfException(50000,"验签失败");
} catch (IllegalArgumentException e) {
e.printStackTrace();
return false;
}
}
/**
* 根据token拿到用户名
* @param token
* @return
*/
public String getUserNameFromToken(String token) {
Claims body = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
return body.getSubject();
}
}自定义未授权
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
ResultDto bean = ResultDto.error("权限不足,请联系管理员!");
bean.setCode(403);
out.write(new ObjectMapper().writeValueAsString(bean));
out.flush();
out.close();
}
}自定义未登录
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
ResultDto bean = ResultDto.error("尚未登录,请登录!或者token令牌无效");
bean.setCode(401);
out.write(new ObjectMapper().writeValueAsString(bean));
out.flush();
out.close();
}
}表数据
注意要跟自己前端vue写的对应起来,还有那个权限值要跟咱们写的controller接口路径对应上





好博客就要一起分享哦!分享海报
此处可发布评论
评论(2)展开评论
您可能感兴趣的博客
他的专栏
他感兴趣的技术

新业务
springboot学习
ssm框架课
vue学习
【带小白】java基础速成