Spring Security
安全
官网:https://docs.spring.io/spring-security/site/docs/current/reference/html5/
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。
Spring Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求
一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
认识SpringSecurity
Spring Security
是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security
模块,进行少量的配置,即可实现强大的安全管理!
记住几个类:
WebSecurityConfigurerAdapter
:自定义Security策略AuthenticationManagerBuilder
:自定义认证策略@EnableWebSecurity
:开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权” (访问控制)。
“认证”(Authentication)
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。
身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。
“授权” (Authorization)
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
这个概念是通用的,而不是只在Spring Security 中存在。
认证和授权
引入模块
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
编写配置类
package com.ice.config;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity // 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
定制请求授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
// 定制请求的授权规则
// 首页所有人可以访问
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
}
开启自动登录功能
// 开启自动配置的登录功能
// /login 请求来到登录页
// /login?error 重定向到这里表示登录失败
http.formLogin();
我们要将前端传过来的密码进行某种方式加密,否则就无法登录
权限控制和注销
开启自动配置的注销的功能
//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//....
//开启自动配置的注销的功能
// /logout 注销请求
http.logout();
// .logoutSuccessUrl("/"); 注销成功来到首页
//http.logout().logoutSuccessUrl("/");
}
记住我
开启记住我功能
//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//。。。。。。。。。。。
//记住我
http.rememberMe();
}
定制登录页
在刚才的登录页配置后面指定 loginpage
http.formLogin().loginPage("/toLogin");
我们配置接收登录的用户名和密码的参数!
http.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLogin")
.loginProcessingUrl("/login"); // 登陆表单提交请求
在登录页增加记住我的多选框
<input type="checkbox" name="remember"> 记住我
后端验证处理!
//定制记住我的参数!
http.rememberMe().rememberMeParameter("remember");
完整配置代码
package com.ice.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//开启自动配置的登录功能:如果没有权限,就会跳转到登录页面!
// /login 请求来到登录页
// /login?error 重定向到这里表示登录失败
http.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/toLogin")
.loginProcessingUrl("/login"); // 登陆表单提交请求
//开启自动配置的注销的功能
// /logout 注销请求
// .logoutSuccessUrl("/"); 注销成功来到首页
http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
http.logout().logoutSuccessUrl("/");
//记住我
http.rememberMe().rememberMeParameter("remember");
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿....
//Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("ice").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
}
}
Shiro
官网:http://shiro.apache.org/
初始化Shrio
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
package com.ice.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author zph
* @description TODO
* @create 2021/11/8 17:36
*/
@Configuration
public class ShiroConfig {
//shiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
return shiroFilterFactoryBean;
}
//DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,需要自定义
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
package com.ice.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* @author zph
* @description TODO
* @create 2021/11/8 17:39
*/
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
ShirConfig
package com.ice.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author zph
* @description TODO
* @create 2021/11/8 17:36
*/
@Configuration
public class ShiroConfig {
//shiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证了才能访问
user:必须拥有记住我才能用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
* */
//拦截
Map<String, String> filterMap = new LinkedHashMap<>();
//授权,
filterMap.put("/admin/add","perms[user:add]");
//设置认证请求
filterMap.put("/admin/login","anon");
filterMap.put("/admin/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
//设置登录请求路径
bean.setLoginUrl("/toLogin");
//未授权页面
// bean.setUnauthorizedUrl("/noAuth");
return bean;
}
//DefaultWebSecurityManager
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,需要自定义
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
UserRealm
package com.ice.config;
import com.ice.pojo.User;
import com.ice.service.UserService;
import com.ice.util.StringUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.Md5CredentialsMatcher;
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.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author zph
* @description TODO
* @create 2021/11/8 17:39
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
//拿到当前登录的这个对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
//
// info.addStringPermission(currentUser.getRole());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
User user = userService.getUserByName(userToken.getUsername());
if (user==null){
return null;
}
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
LoginController
@PostMapping("admin/login")
@ResponseBody
public APIResponse login(HttpServletRequest request,String username, String password){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
String pwd = StringUtil.MD5encode(username + password);
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
try {
subject.login(token);//执行登录方法,如果没有异常就说明ok
User user = userService.login(username, password);
request.getSession().setAttribute("LOGIN_USER",user);
return APIResponse.success();
} catch (UnknownAccountException e) {
String msg = "用户名错误";
return APIResponse.fail(msg);
}catch (IncorrectCredentialsException e){
String msg = "密码错误";
return APIResponse.fail(msg);
}
}