今天跟着这位博主学习了后端注册加密和登陆解密功能。
1.说实话,我没搞懂Shiro框架在springboot中的运用。所以我上代码给自己记录一下自己干了什么:
首先是realm包的Realm类:
package realm;
import com.example.demo.User.User;
import com.example.demo.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
public class Realm extends AuthorizingRealm {
@Autowired
private UserService userService;
// 简单重写获取授权信息方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
return s;
}
// 获取认证信息,即根据 token 中的用户名从数据库中获取密码、盐等并返回
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException{
String userName = token.getPrincipal().toString();
User user = userService.getByName(userName);
if(ObjectUtils.isEmpty(user)){
throw new UnknownAccountException();
}
String passwordInDB = user.getPassword();
String salt = user.getSalt();
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName,passwordInDB, ByteSource.Util.bytes(salt),getName());
return authenticationInfo ;
}
}
接着是User类添加了新的属性:
然后是result包的三个类:
package com.example.demo.result;
public class Result {
private int code;
private String message;
private Object data;
Result(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
package com.example.demo.result;
public enum ResultCode {
SUCCESS(200),
FAIL(400),
UNAUTHORIZED(401),
NOT_FOUND(404),
INTERNAL_SERVER_ERROR(500);
public int code;
ResultCode(int code) {
this.code = code;
}
}
package com.example.demo.result;
public class ResultFactory {
public static Result buildSuccessResult(Object data) {
return buildResult(ResultCode.SUCCESS, "成功", data);
}
public static Result buildFailResult(String message) {
return buildResult(ResultCode.FAIL, message, null);
}
public static Result buildResult(ResultCode resultCode, String message, Object data) {
return buildResult(resultCode.code, message, data);
}
public static Result buildResult(int resultCode, String message, Object data) {
return new Result(resultCode, message, data);
}
}
LoginController类进行更新:
package com.example.demo.controller;
import com.example.demo.User.User;
import com.example.demo.result.Result;
import com.example.demo.result.ResultFactory;
import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.HtmlUtils;
@Controller
public class LoginController{
@Autowired
UserService userService;
// @CrossOrigin
//// @PostMapping(value = "/login")
// @RequestMapping(value = "/login",method = RequestMethod.POST)
// @ResponseBody
// public Result login(@RequestBody User requertUser){
// String username = requertUser.getUsername();
// username = HtmlUtils.htmlEscape(username);
//
// User user = userService.get(username,requertUser.getPassword());
// if (null==user){
// return new Result(400);
// }else {
// return new Result(200);
// }
// }
@CrossOrigin
@PostMapping("/login")
@ResponseBody
public Result login(@RequestBody User requestUser){
String username = requestUser.getUsername();
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,requestUser.getPassword());
try {
subject.login(usernamePasswordToken);
return ResultFactory.buildSuccessResult(usernamePasswordToken);
}catch (AuthenticationException e){
String message = "账号密码错误";
return ResultFactory.buildFailResult(message);
}
}
@CrossOrigin
@PostMapping("/register")
@ResponseBody
public Result register(@RequestBody User user) {
String username = user.getUsername();
String password = user.getPassword();
username = HtmlUtils.htmlEscape(username);
user.setUsername(username);
boolean exist = userService.isExist(username);
if (exist) {
String message = "用户名已被使用";
return ResultFactory.buildFailResult(message);
}
// 生成盐,默认长度 16 位
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
// 设置 hash 算法迭代次数
int times = 2;
// 得到 hash 后的密码
String encodedPassword = new SimpleHash("md5", password, salt, times).toString();
// 存储用户信息,包括 salt 与 hash 后的密码
user.setSalt(salt);
user.setPassword(encodedPassword);
userService.add(user);
return ResultFactory.buildSuccessResult(user);
}
}
一个配置类:
package com.example.demo.config;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import realm.Realm;
@Configuration
public class ShiroConfiguration {
@Bean
public static LifecycleBeanPostProcessor getLifecycleBeanProcessor(){
return new LifecycleBeanPostProcessor();
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(getRealm());
return defaultWebSecurityManager;
}
@Bean
public Realm getRealm(){
Realm realm = new Realm();
realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName(("md5"));
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
登出功能
放代码:
@CrossOrigin
@GetMapping("/logout")
@ResponseBody
public Result logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
String message = "成功退出登录";
return ResultFactory.buildSuccessResult(message);
}
此时我完成了后端的接口拦截:上项目结构图(我修改了哪些部分)
接口拦截主要步骤是配制->引用:
package com.example.demo.interceptor;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//后端拦截接口
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,Object o) throws Exception{
if (HttpMethod.OPTIONS.toString().equals((httpServletRequest.getMethod()))) {
httpServletResponse.setStatus(HttpStatus.NO_CONTENT.value());
return true;
}
Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()&&!subject.isRemembered()){
return false;
}
System.out.println(subject.isRemembered());
System.out.println(subject.isAuthenticated());
return true;
}
private boolean begingWith(String page,String[] requiredAuthPages)
{
boolean result = false;
for(String requiredAuthPage : requiredAuthPages){
if (StringUtils.startsWith(page,requiredAuthPage)){
result = true;
break;
}
}
return result;
}
}
//添加接口拦截器
@Bean
public LoginInterceptor getLoginIntercepter() {
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(getLoginIntercepter()).addPathPatterns("/**").excludePathPatterns("/login");
}
还做了一下rememberMe这个操作:
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey("EVANNIGHTLY_WAOU".getBytes());
return cookieRememberMeManager;
}
@Bean
public SimpleCookie rememberMeCookie(){
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setMaxAge(60);
return simpleCookie;
}
设置是否记住我 false:
设置是否记住我 true:
在这真的感谢这位博主的教程!
有缘人看到这篇文章记得去看这位博主,别看我的,我只是在他的基础上给自己用。