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);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?