SpringSecurity整合JWT
1.数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`name_zh` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_product', '商品管理员');
INSERT INTO `role` VALUES (2, 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'ROLE_user', '用户管理员');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`enabled` tinyint(1) NULL DEFAULT NULL,
`account_non_expired` tinyint(1) NULL DEFAULT NULL,
`account_non_locked` tinyint(1) NULL DEFAULT NULL,
`credentials_non_expired` tinyint(1) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'root', '{noop}123', 1, 1, 1, 1);
INSERT INTO `user` VALUES (2, 'admin', '{noop}123', 1, 1, 1, 1);
INSERT INTO `user` VALUES (3, 'common', '{noop}123', 1, 1, 1, 1);
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`uid` int(0) NULL DEFAULT NULL,
`rid` int(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `uid`(`uid`) USING BTREE,
INDEX `rid`(`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 2, 2);
INSERT INTO `user_role` VALUES (4, 3, 3);
SET FOREIGN_KEY_CHECKS = 1;
2.导入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</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>
3.创建各种类
3.1实体类
实体类应该实现security的用户实体类(UserDetails)
package com.example.securitystu5.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean accountNonExpired;
private Boolean accountNonLocked;
private Boolean credentialsNonExpired;
//private List<Role> roles = new ArrayList<>();
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
3.2dao
@Mapper
public interface UserDAO extends BaseMapper<User> {
}
3.3服务层
用户登陆的 实体类要实现UserDetailsService,security会自动调用loadUserByUsername()方法
@Service
public class MyUserService implements UserDetailsService {
@Autowired
UserDAO userDAO;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
return userDAO.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, userName));
}
}
3.4配置类
@Configuration
public class ScurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
JwtFilter jwtFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//.mvcMatchers("/hello").permitAll()//放行接口
.anyRequest().authenticated()//除了上面的接口都需要认证
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint()) //如果不是表单登录,则响应用户需要认证登录,也就是不显示默认的登录界面
.and().csrf().disable();
http.addFilterAt(LoginFilter(), UsernamePasswordAuthenticationFilter.class);//替换过滤器
http.addFilterBefore(jwtFilter, LoginFilter.class);// 在指定过滤器前添加过滤器
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public LoginFilter LoginFilter() throws Exception {
LoginFilter loginFilter = new LoginFilter();
//1.认证 url
loginFilter.setFilterProcessesUrl("/doLogin");
//2.认证 接收参数
loginFilter.setUsernameParameter("uname");
loginFilter.setPasswordParameter("passwd");
//3.指定认证管理器
loginFilter.setAuthenticationManager(authenticationManagerBean());
//4.指定成功时处理
loginFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler());
//5.认证失败处理
loginFilter.setAuthenticationFailureHandler(new MyAuthenticationFailureHandler());
return loginFilter;
}
}
3.5成功、失败、未授权响应
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "登录成功");
result.put("status", 200);
result.put("token", request.getAttribute("token"));
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Map<String, Object> result = new HashMap<String, Object>();
result.put("msg", "登录失败: "+exception.getMessage());
result.put("status", 500);
response.setContentType("application/json;charset=UTF-8");
String s = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(s);
}
}
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");
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
httpServletResponse.getWriter().println("必须认证之后才能访问!");
}
}
3.6jwt工具类
@Component
public class JwtUtils {
private static final String SING = "123456";
private static final Long EXPIRES =60000*60L;
public static String creatToken(Map<String,String> map){
JWTCreator.Builder jwt = JWT.create();
map.forEach((k,v)->{
jwt.withClaim(k,v);
});
return jwt.withExpiresAt(new Date(System.currentTimeMillis()+EXPIRES)).sign(Algorithm.HMAC256(SING));
}
//解析token
public static DecodedJWT verification(String token) {
return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
}
}
3.7jwt过滤器
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
JwtUtils jwtUtils;
@Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain filterChain) throws IOException, ServletException {
//获得请求体中的token
String token = req.getHeader("token");
//如果不管用户有没有提供的token都需要进行下去
if (!ObjectUtils.isEmpty(token)){
DecodedJWT verification = jwtUtils.verification(token);//解析token
//创建用户对象
User user = new User();
user.setUsername(verification.getClaim("uname").asString());
user.setPassword(verification.getClaim("passwd").asString());
//生成securityToKen
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user,null,null);
//存放token
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
filterChain.doFilter(req,res);
}
filterChain.doFilter(req,res);
}
}
3.8认证过滤器
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
JwtUtils jwtUtils;
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
Map<String, String> userInfo = null;
try {
userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class);
String username = userInfo.get(getUsernameParameter());//用来接收用户名
String password = userInfo.get(getPasswordParameter());//用来接收密码
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
HashMap<String, String> map = new HashMap<>();
map.put("uname",username);
map.put("passwd",password);
request.setAttribute("token",jwtUtils.creatToken(map));
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
e.printStackTrace();
}
throw new AuthenticationServiceException("失败");
}
}