SpringBoot+SpringSecurity+jwt实现认证授权

前言;

网上查看一些博主感觉不是很全,正好在做微服务,特地记录一下,希望能帮助其他人,如果有问题可以留言或者加微信(yswsxf1314)讨论

这边不讲jwt,直接使用,需要熟悉可以关注jwt那边博客,话不多说直接上代码

 

基本坐标

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.11.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <!--        通用mapper-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件
server:

  port: 8001
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
    username: root
    password: root
jwt:
  signKey: NDU0NTY4amhmc3NkeHp6eGNxdzIlMjFAJTIxQCUyM2ZmNQ== #盐


public class UserBean implements Serializable {
    private String roleKey;
    private Long user_id;
    private Long role_id;
    private String username;
    private String password;
  //get,set
}

public class UserPojo  implements Serializable {
    @Id
    @GeneratedValue(generator = "JDBC")
    @Column(name = "user_id")
    private Long user_id;
    @Column(name = "username")
    private String username;
    @Column(name = "password")
    private String password;
    @Column(name = "name")
    private String name;
    @Column(name = "telephone")
    private Long telephone;
    @Column(name = "gender")
    private String gender;
    @Transient
    private String rolePeisList;  //角色权限集合
 //get,set
}

Hendler相关

/*无权访问*/
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
   @Override
   public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
       httpServletResponse.setContentType("application/json;charset=utf-8");
       PrintWriter out = httpServletResponse.getWriter();
       out.write("{\"code\":\"40001\",\"msg\":\"无权访问\"}");
   }
}

//未登录
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e)
            throws IOException, ServletException {
        httpServletResponse.setContentType("application/json;charset=utf-8");
        PrintWriter out = httpServletResponse.getWriter();
        out.write("{\"code\":\"40001\",\"msg\":\"无访问权限,请先登录\"}");
    }
}

//退出成功
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        httpServletResponse.setContentType("application/json;charset=utf-8");
        PrintWriter out = httpServletResponse.getWriter();
        out.write("{\"code\":\"100\",\"msg\":\"退出成功\"}");
    }
}

security配置

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
  private  MyAuthenticationEntryPoint  myauthenticationEntryPoint;  //未登陆时返回 JSON
    @Resource
    private  MyLogoutSuccessHandler  mylogoutSuccessHandler;  // 注销成功返回的JSON
    @Resource
    private  MyAccessDeniedHandler  myaccessDeniedHandler;    // 无权访问返回的
    @Resource
    private UserDetailServiceImpl myUserDetailsService;


    /*** 当前配置为form表单登录认证方式*/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http .csrf().disable()//禁用跨站csrf攻击防御
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)  //前后端分离采用JWT ,不需要session
                .and()
                .authorizeRequests()//配置登录后的权限
                .antMatchers("/login","/success").permitAll()// 用户可任意访问
                .anyRequest().authenticated() // 除上面外的请求全部需要鉴权认证-;//authenticated()要求在执行请求要求必须已登录
                .and()
       .formLogin()   //开启登录
                .loginProcessingUrl("/login")
                .and()
                .httpBasic().authenticationEntryPoint(myauthenticationEntryPoint)  //无访问权限,请先登录
                .and()
                .addFilter(new JwtAuthenticateFilter(authenticationManager()))
                .addFilter(new JwtAuthorizationFilter(authenticationManager()))
            .logout()
                .logoutUrl("/logout")  //退出
                .logoutSuccessHandler(mylogoutSuccessHandler).permitAll();
            http.exceptionHandling().accessDeniedHandler( myaccessDeniedHandler);//无权访问返回的
    };

    // 配置数据库认证方法, http basic登录基础配置
    @Override
    public  void  configure(AuthenticationManagerBuilder auth)  throws  Exception {
        //调用DetailsService完成用户身份验证              设置密码加密方式
       auth.userDetailsService(myUserDetailsService).passwordEncoder(getBCryptPasswordEncoder() );
  }
    @Bean
    public BCryptPasswordEncoder getBCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

Controller层,用于测试,不链接数据库,需要自行添加

@RestController
public class HomeController {

// 登录成功首页
@PostMapping("/success")
public String login() {
/*
* 后期这里设置生产jwt
* */
System.out.println("登录成功首页");
return "登录成功首页";
}

//增加用户
@PostMapping("/add")
@Secured({"ROLE_asiaPacificGroupLeader"})//角色 专门用于判断是否具有角色,能写在方法或者类上,参数要以ROLE_ 开头
public String add(){
System.out.println("增加用户");
return "增加用户";
}
//删除用户
@PostMapping("/delete")
@Secured({"ROLE_asi"}) //角色
public String delete(){
System.out.println("删除用户");
return "删除用户";
}
//编辑用户
@PostMapping("/edit")
@PreAuthorize("hasAnyAuthority('leader')") //权限
public String edit(){
System.out.println("编辑用户");
return "编辑用户";
}
//查找用户
@PostMapping("/select")
@PreAuthorize("hasAnyAuthority('asiaPa')") //权限
public String orderList(){
System.out.println("查找用户");
return "查找用户";
}
//观看用户
@PostMapping("/find")
public String find(){
System.out.println("观看用户");
return "观看用户";
}
}

  登录和校验过滤器

public class JwtAuthenticateFilter extends UsernamePasswordAuthenticationFilter {

private final AuthenticationManager authenticationManager;

public JwtAuthenticateFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}

//用户发起请求,拦截,获取用户名密码
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

String username=request.getParameter("username");
String password=request.getParameter("password");
UsernamePasswordAuthenticationToken uToken=new UsernamePasswordAuthenticationToken(username,password);
//用户认证
return this.authenticationManager.authenticate(uToken);
}

@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return super.requiresAuthentication(request, response);
}

@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write("{\"code\":4000,\"msg\":\"登录失败\"}");
}

//校验证成功,将token 信息返回给用户
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
User user =(User) authResult.getPrincipal();
List<String> roles = user.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
String token = JwtTokenUtil.build(user.getUsername(), user.getPassword() ,roles);
response.setHeader("Authorization",token);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
String result= "{\"code\":4000,\"msg\":\"登录成功\",\"data\":"+user.getUsername()+"}";;
out.write(result);

}
}

//校验jwt是否有效,是否有权限
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
Authentication authentication= getAuthentication(request);
if(authentication==null){
filterChain.doFilter(request,response);
return;
}
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request,response);

}

private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request){
String token = request.getHeader("Authorization");
if(!StringUtils.isEmpty(token)){//&&token.startsWith("Bearer"
String signKey="NDU0NTY4amhmc3NkeHp6eGNxdzIlMjFAJTIxQCUyM2ZmNQ==";//盐
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
} catch (Exception e) {
return null;
}
String username =claims.getSubject();
List<String> roles = (List<String>) claims.get("roles");
List<SimpleGrantedAuthority> collect = roles.stream().map(authority -> new SimpleGrantedAuthority((String) authority)).collect(Collectors.toList());

if(!StringUtils.isEmpty(username)){
return new UsernamePasswordAuthenticationToken(username,null,collect);
}
}
return null;
}
}
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;

@Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
// 查询数据库,角色权限,自己虚拟数据,就不在这加了
UserPojo userPojo = userService.selectUserByUserName(username);
if (userPojo == null) {
throw new UsernameNotFoundException("登录用户:" + username + "不存在");
}
String rolePeisList = userPojo.getRolePeisList();//角色权限列表
System.out.println("rolePeisList==="+rolePeisList);
//将数据库的roles解析为UserDetails的权限集
//AuthorityUtils.commaSeparatedStringToAuthorityList将逗号分隔的字符集转成权限对象列表
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList(rolePeisList);
return new User(userPojo.getUsername(), userPojo.getPassword(), auths);
}
}
jwt令牌生产工具
@Component
public class JwtTokenUtil implements Serializable {
//生产jwt
public static String build( String username ,String telephone , List<String> roles) {
//签名算法,选择SHA-256
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

String signKey="NDU0NTY4amhmc3NkeHp6eGNxdzIlMjFAJTIxQCUyM2ZmNQ==";//盐

//获取当前系统时间
Date now = new Date(System.currentTimeMillis());
Date exp = new Date(System.currentTimeMillis()+1000*60*60);
JSONObject payLoad = new JSONObject();
payLoad.put("username",username);
payLoad.put("telephone",telephone);
payLoad.put("roles",roles);
//jwt
JwtBuilder jwt = io.jsonwebtoken.Jwts.builder()
.setClaims(payLoad)//自定义内容
.setId(UUID.randomUUID().toString()) //每个jwt的唯一标识
.setIssuedAt(now) //签发时间
.setExpiration(exp) //过期时间
.setSubject(username) //可以放用户名,用户唯一标识
.signWith(signatureAlgorithm, signKey);

String compact = jwt.compact();

return compact;
}
@SpringBootApplication
@MapperScan(value = "eview.dao")
@EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled = true) //开启两种角色和权限
public class SpringSecurityApplication {

public static void main(String[] args) {
SpringApplication.run(SpringSecurityApplication.class, args);
}

}
如果不想拦截授权登录,也可以接口授权登录,先在security放行接口,然后controlelr写接口,代码展示;

@PostMapping("/login")
@ApiOperation("用户登录")
public Result userlogin(@RequestBody UserBean userBean) {
try {
String username = userBean.getUsername();
String password = userBean.getPassword();
if (username == null || password == null) {
return new Result(false, MessageConstant.USELOGIN_USERBEAN_NULL);
}
UserDetails userDetails = userDetailService.loadUserByUsername(username);
if (userDetails == null || !username.equals(userDetails.getUsername())) {
return new Result(false, MessageConstant.USELOGIN_USERNAME_FALSE);
}
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
if (!encoder.matches(password, userDetails.getPassword())) {
return new Result(false, MessageConstant.USELOGIN_PASSWORD_FALSE);
};
UserEntity userlogin = userService.userlogin(username);
String telephone = userlogin.getTelephone().toString();
List<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
return getResult(username, userlogin, telephone, roles);
} catch (Exception e) {
e.printStackTrace();
log.error(e);
return new Result(false,MessageConstant.USELOGIN_ERROR);
}
}
public Result getResult(String username, UserEntity userlogin, String telephone, List<String> roles) {
String token = JwtTokenUtil.build(username, telephone,roles);
Map<String, Object> userJwt = new HashMap<>();
userJwt.put("token", token);
userJwt.put("user_id", userlogin.getUser_id());
redisTemplate.opsForValue().set(username, token);
redisTemplate.expire(username, 10080, TimeUnit.MINUTES);
return new Result(true, MessageConstant.USELOGIN_SUCCESS, userJwt);
}


 

posted @   心愿路自平  阅读(356)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示