Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
项目代码片段
记一次项目中使用的Spring security进行权限验证。admin,distrAdmin,reseller这三个权限为例。
安装mysqld服务器,输入命令:mysqld --install,启动mysql服务:net start mysql
加入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
控制层:AuthController.java
@Slf4j
@RestController
@RequestMapping("/user")
@Api(value = "web管理页面,用户认证 login")
public class AuthController {
@Autowired
private AuthService authService;
@PostMapping(value = "/login")
@ApiOperation(value = "用户登陆", notes = "User")
public ResponseEntity<?> login(@RequestBody @Valid UserVo userVo) {
return authService.login(userVo);
}
@PostMapping(value = "/logout")
@ApiOperation(value = "用户登出", notes = "User")
@PreAuthorize("hasAnyRole('admin', 'distrAdmin', 'distrOperator', 'reseller')")
public ResponseEntity<?> logout(@ApiIgnore @AuthenticationPrincipal UserBrief brief) {
return authService.logout(brief);
}
@GetMapping(value = "/me")
@ApiOperation(value = "用户认证", notes = "User")
@PreAuthorize("hasAnyRole('admin', 'distrAdmin', 'distrOperator', 'reseller')")
public ResponseEntity<?> me(@ApiIgnore @AuthenticationPrincipal UserBrief user) {
return authService.me(user);
}
}
AuthService.java
AuthServiceImpl.java
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {
private static final String DEFAUL_AUTHORITY_PREFIX = "ROLE_";
@Autowired
private DomainPasswordEncoder passwordEncoder;
@Autowired
private UserService userService;
@Override
public ResponseEntity<?> login(UserVo userVo) {
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserName, userVo.getName()));
if (ComUtil.isNull(user) || user.getUserType().isAppUser()) {
throwBadRequest(LOGIN_FAIL);
}
boolean isMatch = passwordEncoder.match(userVo.getPassword(), user.getPassword());
if (!isMatch) {
throwBadRequest(LOGIN_FAIL);
}
Date now = new Date();
//set login state: On=1 setting Sign in status On=1
user.setStatus(Constant.MGR_USER_ON);
String token = GenerationSequenceUtil.generateUUID("").substring(0, 16);
userService.lambdaUpdate().set(User::getStatus, Constant.MGR_USER_ON)
.set(User::getToken, token)
.set(User::getTokenExpireDate, DateTimeUtil.addDays(now, 7))
.eq(User::getId, user.getId()).update();
String sessionKey = DesUtil.encrypt(String.format("%s%s", token, user.getUserName()));
return ok(new UserLoginVo(user.getUserType(), sessionKey));
}
@Override
public ResponseEntity<?> logout(UserBrief brief) {
log.info("logout:{u:{}-{}}", brief.getId(), brief.getUserName());
userService.lambdaUpdate().set(User::getStatus, Constant.APP_USER_OFF)
.set(User::getToken, null)
.set(User::getTokenExpireDate, null)
.eq(User::getId, brief.getId()).update();
return ok().build();
}
@Override
public ResponseEntity<?> me(UserBrief user) {
User found = userService.getById(user.getId());
UserMeVo me = new UserMeVo(found.getUserType(), found.getDistrId(), found.getResellerId());
return ok(me);
}
@Override
public Authentication checkLogin(String userName) {
User auth = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getUserName, userName)
.gt(User::getTokenExpireDate,new Date()));
if(ComUtil.isEmpty(auth)){
return null;
}
return buildAuthentication(auth, auth.getToken());
}
private Authentication buildAuthentication(User user, String token) {
List<GrantedAuthority> authorities = Lists.newArrayList(
new SimpleGrantedAuthority(DEFAUL_AUTHORITY_PREFIX + user.getUserType().name()));
PreAuthenticatedAuthenticationToken authentication = new PreAuthenticatedAuthenticationToken(user.toBrief(),
user.getPassword(), authorities);
authentication.setDetails(token);
return authentication;
}
}
TokenAuthenticationProvider.java
public class TokenAuthenticationProvider implements AuthenticationProvider {
public TokenAuthenticationProvider() {
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Optional<String> username = (Optional) authentication.getPrincipal();
Optional<String> token = (Optional) authentication.getCredentials();
emptyValidation(username, token);
Class cls = null;
Object resObj = null;
try {
cls = Class.forName("jp.co.tenfeetwright.sum.dmc.service.impl.AuthServiceImpl");
Object obj = SpringUtil.getBean("authServiceImpl", cls);
Method mth = ReflectionUtils.findMethod(obj.getClass(), "checkLogin", String.class);
resObj = ReflectionUtils.invokeMethod(mth, obj, username.get());
} catch (Exception e) {
e.printStackTrace();
}
if(ComUtil.isEmpty(resObj)){
throw new BadCredentialsException("Invalid token or token expired");
}
Authentication result = (Authentication) resObj;
Optional<Object> auth = Optional.ofNullable(resObj);
if (!auth.isPresent() || auth.get() == null) {
throw new BadCredentialsException("Invalid token or token expired");
}
if (!result.getDetails().equals(token.get())) {
throw new BadCredentialsException("Invalid token or token expired");
}
return result;
}
private void emptyValidation(Optional<String> username, Optional<String> token) {
if (!username.isPresent() || username.get().isEmpty()) {
throw new BadCredentialsException("Invalid token");
}
if (!token.isPresent() || token.get().isEmpty()) {
throw new BadCredentialsException("Invalid token");
}
}
@Override
public boolean supports(Class<?> aClass) {
return aClass.equals(PreAuthenticatedAuthenticationToken.class);
}
}
注意:因为没有使用过这个,在之前写过的一个访问/login的页面跳转现在不起作用了(包括其他的接口也都被拦截,无法访问),就是因为security内置了一个login页面。
以上代码不全,截取的项目中的代码。
学习Demo
以下是我写的一个可用的Demo,自取:
https://download.csdn.net/download/weixin_45044097/13104660