公司新来了一个同事,把权限系统设计的炉火纯青!(二)

简介: 公司新来了一个同事,把权限系统设计的炉火纯青!(二)

创建一个 JwtUser 实现 UserDetails

创建 一个相关的 JavaBean

package com.example.demo;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class JwtUser implements UserDetails {
    private String username;
    private String password;
    private Integer state;
    private Collection<? extends GrantedAuthority> authorities;
    public JwtUser(){
    }
    public JwtUser(String username, String password, Integer state, Collection<? extends GrantedAuthority> authorities){
        this.username = username;
        this.password = password;
        this.state = state;
        this.authorities = authorities;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }
    @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;
    }
}

编写工具类生成令牌

编写工具类,用来生成 token,以及刷新 token,以及验证 token。

package com.example.demo;
public class JwtTokenUtil implements Serializable {
    private String secret;
    private Long expiration;
    private String header;
    private String generateToken(Map<String, Object> claims) {
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
    }
    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>(2);
        claims.put("sub", userDetails.getUsername());
        claims.put("created", new Date());
        return generateToken(claims);
    }
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }
    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;
    }
    public Boolean validateToken(String token, UserDetails userDetails) {
        JwtUser user = (JwtUser) userDetails;
        String username = getUsernameFromToken(token);
        return (username.equals(user.getUsername()) && !isTokenExpired(token));
    }
}

编写拦截器

编写 Filter 用来检测 JWT

package com.example.demo;
@Component
public class JwtAuthenticationTokenFilter  extends OncePerRequestFilter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String authHeader = httpServletRequest.getHeader(jwtTokenUtil.getHeader());
        if (authHeader != null && StringUtils.isNotEmpty(authHeader)) {
            String username = jwtTokenUtil.getUsernameFromToken(authHeader);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                if (jwtTokenUtil.validateToken(authHeader, userDetails)) {
                    UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

编写 userDetailsService 的实现类

在上方代码中,编写 userDetailsService,类,实现其验证过程

package com.example.demo;
@Service
public class JwtUserDetailsServiceImpl  implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userMapper.selectByUserName(s);
        if (user == null) {
            throw new UsernameNotFoundException(String.format("'%s'.这个用户不存在", s));
        }
        List<SimpleGrantedAuthority> collect = user.getRoles().stream().map(Role::getRolename).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
        return new JwtUser(user.getUsername(), user.getPassword(), user.getState(), collect);
    }
}

编写登录

编写登录业务的实现类 其 login 方法会返回一个 JWTUtils 的 token

@Service
public class UserServiceImpl  implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    public User findByUsername(String username) {
        User user = userMapper.selectByUserName(username);
        return user;
    }
    public RetResult login(String username, String password) throws AuthenticationException {
        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
        final Authentication authentication = authenticationManager.authenticate(upToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        return new RetResult(RetCode.SUCCESS.getCode(),jwtTokenUtil.generateToken(userDetails));
    }
}

最后配置 Config

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());
    }
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
                .and().headers().cacheControl();
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
        registry.requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
    }
    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowCredentials(true);
        cors.addAllowedOrigin("*");
        cors.addAllowedHeader("*");
        cors.addAllowedMethod("*");
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", cors);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

运行,返回 token

运行,返回结果为 token

微信图片_20220908132239.jpg

这里配置 SpringSecurity 之 JSON 登录

这里需要重写 UsernamePasswordAnthenticationFilter 类,以及配置 SpringSecurity

重写 UsernamePasswordAnthenticationFilter

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if(request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE)
                ||request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){
            ObjectMapper mapper = new ObjectMapper();
            UsernamePasswordAuthenticationToken authRequest = null;
            try (InputStream is = request.getInputStream()){
                AuthenticationBean authenticationBean = mapper.readValue(is,AuthenticationBean.class);
                authRequest = new UsernamePasswordAuthenticationToken(
                        authenticationBean.getUsername(), authenticationBean.getPassword());
            }catch (IOException e) {
                e.printStackTrace();
                authRequest = new UsernamePasswordAuthenticationToken(
                        "", "");
            }finally {
                setDetails(request, authRequest);
                return this.getAuthenticationManager().authenticate(authRequest);
            }
        }
        else {
            return super.attemptAuthentication(request, response);
        }
    }
}

配置 SecurityConfig

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .cors().and()
            .antMatcher("/**").authorizeRequests()
            .antMatchers("/", "/login**").permitAll()
            .anyRequest().authenticated()
            .and().formLogin().loginPage("/")
            .and().csrf().disable();
    http.addFilterAt(customAuthenticationFilter(),
    UsernamePasswordAuthenticationFilter.class);
}
@Bean
CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
    CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
    filter.setAuthenticationSuccessHandler(new SuccessHandler());
    filter.setAuthenticationFailureHandler(new FailureHandler());
    filter.setFilterProcessesUrl("/login/self");
    filter.setAuthenticationManager(authenticationManagerBean());
    return filter;
}

这样就完成使用 json 登录 SpringSecurity

需要在 Config 类中配置如下内容

@Bean
  public BCryptPasswordEncoder passwordEncoder(){
      return new BCryptPasswordEncoder();
  }

即,使用此方法,对密码进行加密, 在业务层的时候,使用此加密的方法

@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Resource
    private UserRepository userRepository;
    @Resource
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    @Override
    public User add(User user) {
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
        User user2 = userRepository.save(user);
        return user2;
    }
    @Override
    public ResultInfo login(User user) {
        ResultInfo resultInfo=new ResultInfo();
        User user2 = userRepository.findByName(user.getName());
        if (user2==null) {
            resultInfo.setCode("-1");
            resultInfo.setMessage("用户名不存在");
            return resultInfo;
        }
        if (!bCryptPasswordEncoder.matches(user.getPassword(),user2.getPassword())) {
            resultInfo.setCode("-1");
            resultInfo.setMessage("密码不正确");
            return resultInfo;
        }
        resultInfo.setMessage("登录成功");
        return resultInfo;
    }
}

即,使用 BCryptPasswordEncoder 对密码进行加密,保存数据库

这里使用数据库认证 SpringSecurity

设计数据表

这里设计数据表

微信图片_20220908132339.jpg

着重配置 SpringConfig

@Configurable
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserService userService;
    @Bean
    PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login").permitAll()
                .and()
                .csrf().disable();
    }
}

这里着重配置 SpringConfig。

着重讲解了 RBAC 的权限配置,以及简单的使用 SpringSecurity,以及使用 SpringSecurity + JWT 完成前后端的分离,以及配置 json 登录,和密码加密方式。

相关文章
|
NoSQL 数据可视化 JavaScript
搭建easy-mock数据模拟服务器
搭建easy-mock数据模拟服务器
搭建easy-mock数据模拟服务器
|
存储 Linux Docker
docker在欧拉服务器上编译安装应该注意什么?如何操作?
【10月更文挑战第31天】docker在欧拉服务器上编译安装应该注意什么?如何操作?
500 2
|
3月前
|
机器学习/深度学习 人工智能 资源调度
大语言模型的核心算法——简要解析
大语言模型的核心算法基于Transformer架构,以自注意力机制为核心,通过Q、K、V矩阵动态捕捉序列内部关系。多头注意力增强模型表达能力,位置编码(如RoPE)解决顺序信息问题。Flash Attention优化计算效率,GQA平衡性能与资源消耗。训练上,DPO替代RLHF提升效率,MoE架构实现参数扩展,Constitutional AI实现自监督对齐。整体技术推动模型在长序列、低资源下的性能突破。
473 8
|
10天前
|
前端开发 安全 Java
Hello AgentScope Java
AgentScope Java v0.2 发布,支持 ReAct 智能体核心能力,提供高代码透明性、多模态消息、钩子机制、会话持久化与结构化输出,助力开发者高效构建可追溯、易调试的 Agent 应用。
199 1
|
2月前
|
机器学习/深度学习 人工智能 自然语言处理
拔俗当AI成为你的“心灵哨兵”:多模态心理风险预警系统如何工作?
AI多模态心理预警系统通过融合表情、语调、文字、绘画等多维度数据,结合深度学习与多模态分析,实时评估心理状态。它像“心灵哨兵”,7×24小时动态监测情绪变化,发现抑郁、焦虑等风险及时预警,兼顾隐私保护,助力早期干预,用科技守护心理健康。(238字)
|
3月前
|
存储 人工智能 弹性计算
阿里云权益中心详解:个人开发者与企业用户和高校学生与教师的综合优惠平台
阿里云权益中心是什么?简单来说,它是一个致力于为高校学生和教师、个人开发者、企业用户提供优惠上云和快速上云的平台,本文将深度解析权益中心的核心活动、适用场景及参与方式,以供您了解和参考。
|
Linux Windows Ubuntu
windows系统下FileZilla客户端 尝试连接“ECONNREFUSED - 连接被服务器拒绝”失败。 解决方案
windows系统下使用FileZilla连接linux(虚拟机系统)总是报错:尝试连接“ECONNREFUSED - 连接被服务器拒绝”失败。 个人使用的是最新版FileZilla:FileZilla-3.31.0。
11193 0
|
存储 编译器 Go
Golang深入浅出之-掌握Go语言Map:初始化、增删查改与遍历
【4月更文挑战第21天】Go语言中的`map`提供快速的键值对操作,包括初始化、增删查改和遍历。初始化时,推荐使用`make()`函数,如`make(map[string]int)`。插入和查询键值对直接通过索引访问,更新则重新赋值。删除键值对需用`delete()`函数,确保键存在。遍历map常用`for range`,注意避免在遍历中修改map。了解这些并避免易错点,能提升代码效率和可读性。
344 1
Golang深入浅出之-掌握Go语言Map:初始化、增删查改与遍历
|
人工智能 运维 自然语言处理
AI战略丨构建未来:生成式人工智能技术落地策略
GenAI 的技术落地需要企业进行周密地规划和持续地努力。企业必须从自身的战略出发,综合考虑成本、效果和性能,制定合理的技术架构,通过全面的 AI 治理,实现可持续的创新和发展。
|
机器学习/深度学习 数据采集 算法
【机器学习】K-Means聚类的执行过程?优缺点?有哪些改进的模型?
K-Means聚类的执行过程、优缺点,以及改进模型,包括K-Means++和ISODATA算法,旨在解决传统K-Means算法在确定初始K值、收敛到局部最优和对噪声敏感等问题上的局限性。
348 2