SpringSecurity与JWT整合

本文介绍如何在Spring Security框架中整合JSON Web Tokens (JWT),实现用户认证与授权。通过数据库验证用户信息,并使用JWT进行身份令牌管理。文章详细展示了项目配置、过滤器实现及测试过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringSecurity与JWT整合

数据库基于:SpringSecurity从数据库中获取用户信息进行验证

依赖:

<dependencies>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.21</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

application.properties:

spring.thymeleaf.cache=false
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springsecurity001?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.mapper-locations=classpath:mapper/*.xml

启动类SecurityJwtApplication:

@SpringBootApplication
@MapperScan("com.blu.mapper")
public class SecurityJwtApplication {

	public static void main(String[] args) {
		SpringApplication.run(SecurityJwtApplication.class, args);
	}
	
	@Bean
	public BCryptPasswordEncoder bcryptPasswordEncoder(){
		return new BCryptPasswordEncoder();
	}

}

MyUser:

@Data
public class MyUser implements UserDetails {
	
	private Integer id;
	private String name;
	private String password;
	private List<MyRole> roles;

	@JsonIgnore
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return roles;
	}

	@JsonIgnore
	@Override
	public String getUsername() {
		return name;
	}

	@JsonIgnore
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}

	@JsonIgnore
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}

	@JsonIgnore
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}

	@JsonIgnore
	@Override
	public boolean isEnabled() {
		return true;
	}

}

MyRole:

@Data
public class MyRole implements GrantedAuthority {
	
	private Integer id;
	private String name;

	@JsonIgnore
	@Override
	public String getAuthority() {
		return name;
	}

}

MyUserMapper:

public interface MyUserMapper {
	MyUser findByName(String name);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.blu.mapper.MyUserMapper">
	<resultMap type="com.blu.entity.MyUser" id="myUserMap">
		<id column="uid" property="id"></id>
		<result column="uname" property="name"></result>
		<result column="password" property="password"></result>
		<collection property="roles" ofType="com.blu.entity.MyRole">
			<id column="rid" property="id" />
			<result column="rname" property="name" />
		</collection>
	</resultMap>

	<select id="findByName" parameterType="String"
		resultMap="myUserMap">
		select u.id uid,u.name uname,u.password,r.id rid,r.name rname
		from user u,role r,role_user ur 
		where u.name = #{name} and ur.user_id = u.id and ur.role_id = r.id
	</select>
</mapper>

UserService:

public interface UserService extends UserDetailsService {

}

UserServiceImpl:

@Service
public class UserServiceImpl implements UserService {
	
	@Autowired
	private MyUserMapper myUserMapper;

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		MyUser myUser = myUserMapper.findByName(username);
		return myUser;
	}

}

JwtLoginFilter:

public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {
	
	public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {

		super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
		setAuthenticationManager(authenticationManager);
	}


	/**
	 * 从登录参数json数据中获取用户名密码,然后调用AuthenticationManager.authenticate()方法进行校验。
	 */
	@Override
	public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse resp) throws AuthenticationException, IOException, ServletException {
		//将用户传的json数据转为user对象
		MyUser user = new ObjectMapper().readValue(req.getInputStream(),MyUser.class);
		return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(),user.getPassword()));
	}

	/**
	 * 校验成功
	 */
	@Override
	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
		Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
		StringBuffer sb = new StringBuffer();
		for (GrantedAuthority authority : authorities) {
			sb.append(authority.getAuthority()).append(",");
		}
		String jwt = Jwts.builder()
				.claim("authorities", sb)
				.setSubject(authResult.getName())
				.setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000))//设置过期时间
				.signWith(SignatureAlgorithm.HS512, "root@123")//设置加密方式,以及key
				.compact();
		//设置登录成功后返回的信息
		Map<String,String> map = new HashMap<>();
		map.put("token",jwt);
		map.put("msg","登录成功");
		response.setContentType("application/json;charset=utf-8");
		PrintWriter writer = response.getWriter();
		writer.write(new ObjectMapper().writeValueAsString(map));
		writer.flush();
		writer.close();
	}

	/**
	 * 校验失败
	 */
	@Override
	protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
		Map<String,String> map = new HashMap<>();
		map.put("msg","登录失败");
		response.setContentType("application/json;charset=utf-8");
		PrintWriter writer = response.getWriter();
		writer.write(new ObjectMapper().writeValueAsString(map));
		writer.flush();
		writer.close();
	}
}

JwtFilter:

public class JwtFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       HttpServletRequest req =  (HttpServletRequest) servletRequest;
       //从请求头中获取token
        String jwtToken = req.getHeader("authorization");
        Jws<Claims> jws = Jwts.parser().setSigningKey("root@123")
                .parseClaimsJws(jwtToken.replace("Bearer", ""));
        Claims claims = jws.getBody();
        String username = claims.getSubject();
        //获取角色
        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList((String) claims.get("authorities"));
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username,"",authorities);
        SecurityContextHolder.getContext().setAuthentication(token);
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

测试接口:

@RestController
public class StringController {
	
	@GetMapping("hello")
	public String hello() {
		return "hello BLU!";
	}
	
	@GetMapping("hi")
	public String hi() {
		return "hi everyone!";
	}
	
	@GetMapping("admin")
	public String admin() {
		return "hello admin!";
	}

}

Security配置类:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Autowired
	private UserServiceImpl userServiceImpl;
	@Autowired
	private BCryptPasswordEncoder bcryptPasswordEncoder;
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http.authorizeRequests()
			.antMatchers(HttpMethod.GET,"/hi").permitAll()
			.antMatchers("/hello").hasRole("vip1")
			.antMatchers("/admin").hasRole("admin");
		
		http.formLogin().loginPage("/tologin")
						.usernameParameter("name")
						.passwordParameter("password")
						.loginProcessingUrl("/login");
		
		http.csrf().disable();
		
		http.addFilterBefore(new JwtLoginFilter("/login",authenticationManager()),
                UsernamePasswordAuthenticationFilter.class)
        	.addFilterBefore(new JwtFilter(),UsernamePasswordAuthenticationFilter.class);
		
	}
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		
		auth.userDetailsService(userServiceImpl).passwordEncoder(bcryptPasswordEncoder);
	}
	
}

测试错误密码登录:
在这里插入图片描述
测试正确密码登录:
在这里插入图片描述
测试访问(有权限):
在这里插入图片描述
测试访问(无权限):
在这里插入图片描述
测试访问(错误token):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值