Springboot整合Shiro实现登录验证
1 关于Shiro
Apache Shiro 是一个Java的安全框架,主要有三个核心的组件:
- Subject:指当前的操作用户。
- SecurityManager:安全管理器,Shiro通过它来管理内部组件。
- Realm:用于权限的验证,需要自己实现。
2 步骤
首先引入Shiro的maven:
<!—整合shiro—> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.4.0</version> </dependency> |
在application.properties里添加Shrio的配置信息:
- #1.第一行表示是否允许将sessionld 放到cookie 中 shiro.sessionManager.sessionIdCookieEnabled=true #2第二行表示是否允许将sessionld 放到Url地址拦中 shiro.sessionManager.sessionIdUrlRewritingEnabled=true #3.第三行表示访问未获授权的页面时,默认的跳转路径 shiro.unauthorizedUrl=/login #4.第四行表示开启shiro shiro.web.enabled=true #5.第五行表示登录成功的跳转页面 shiro.successUrl=/index #6.第六行表示登录页面 shiro.loginUrl=/doLogin
|
需要实现一个Realme类,继承AuthorizingRealm,并实现它的方法,其中一个是AuthorizationInfo,需要自己写对于权限的管理,另一个是AuthenticationInfo,这里是写登录验证。具体代码如下:
- public class UserRealm extends AuthorizingRealm {
@Autowired
private StudentServiceImpl studentService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = authenticationToken.getPrincipal().toString();
Optional<Student> studentOptional = studentService.findStudentBySno(username);
if (studentOptional.isPresent()) {
Student student = studentOptional.get();
return new SimpleAuthenticationInfo(username, student.getPassword(), ByteSource.Util.bytes(username), getName());
}
throw new UnknownAccountException(“不存在“);
}
}
|
登录验证是先获取到username,然后查到该用户的相关信息,如果不存在则返回一个UnknownAccountException异常。存在则返回一个SimpleAuthenticationInfo,参数是username、数据库查到的密码、还有盐值和用户名。接着需要配置Shiro:
- @Configuration
public class ShiroConfig {
@Bean
UserRealm userRealm() {
return new UserRealm();
}
@Bean
DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm());
return defaultWebSecurityManager;
}
@Bean
ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition = new DefaultShiroFilterChainDefinition();
defaultShiroFilterChainDefinition.addPathDefinition(“/doLogin”, “anon”);
defaultShiroFilterChainDefinition.addPathDefinition(“/**”, “authc”);
return defaultShiroFilterChainDefinition;
}
}
|
将刚刚的UserReaml交给SecurityManager进行管理。并且设置过滤器,比如哪些接口是需要登录后或者某种权限才能访问的。其中 anon 表示匿名即游客就能访问,这里设置了登录接口。authc是需要登录验证后才能访问。除了这两种还有其他:authcBasic、logout、noSessionCreation、perms、port、rest、roles、ssl、user。如果用户开启了RememberMe,则当用户关闭浏览器,下次访问时,不再是authc,而是user。因为authc是需要重新认证的。User就表示该用户曾经被Shiro记住过。
接下来写LoginController:
- @PostMapping(“/doLogin”)
public Result<Object> doLogin(@RequestBody UserToken userToken) {
Subject subject = SecurityUtils.getSubject();
String pwd = ShiroKit.mds(String.valueOf(userToken.getPassword()), userToken.getUsername());
UsernamePasswordToken token = new UsernamePasswordToken(userToken.getUsername(), pwd);
String sessionId = (String) subject.getSession().getId();
try {
subject.login(token);
Map<String, String> map = new HashMap<String, String>();
map.put(“sessionId”, sessionId);
map.put(“username”, userToken.getUsername());
return common.SUCCESS(map);
} catch (Exception e) {
return common.ERROR(codeEnum.getERR_PWD(), “用户名或密码错误“, “”);
}
}
|
因为密码不是明文储存在数据库中的,所以这里采用了MD5加密,没有使用Shiro的加密方式。需要将获取到的密码进行再次加密后和数据库进行比对验证。
加密方式:
- public class ShiroKit {
public static String mds(String password, String salt) {
return new Md5Hash(password, salt, 1).toHex();// 加密一次
}
}
|
这里subject.login则会调用UserReaml,将用户名和加密后的密码传过去进行验证。如果有错误,比如密码不正确或者用户不存在就给前端返回一个用户名或密码不正确的提示。
登出:
- @GetMapping(“/logout”)
public Result logout() {
SecurityUtils.getSubject().logout();
return common.SUCCESS(“”);
}
|
3**总结**
这里只是简单运用了shiro实现了登录验证。没有去分析源码,有需要的朋友可以自行去官方查阅。
刘玉江
2020年09月27日
还没有评论,来说两句吧...