1.pom依赖
第一步肯定是添加引用的包。
<!-- Spring Security-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.0.1.RELEASE</version>
</dependency>
<!-- Spring Security方法级权限控制注解-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
2.web.xml
第二步肯定是从web启动这个框架。
<!-- SpringSecurity过滤器链 -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:如果有spring的话,要配置在一块。
其实就是在配置spring的基础上,扫描文件的时候顺便把spring security的xml文件扫描进来就行了。本质就是加一行字的事,很简单
<!--启动spring容器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 指定spring和spring-security配置文件位置 -->
<param-value>
classpath:applicationContext.xml
classpath:spring-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
3.配置spring-security.xml
简单配置(就是将用户名写死在内存中,不连接数据库,进行一个简单的认证):
登录页面框架已经自带。
我们就规定它要对哪些页面进行安全验证就行了。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!-- autoconfig使用默认过滤配置 use-expressions是否使用SPEL表达式(没学过) -->
<security:http auto-config="true" use-expressions="false">
<!-- 配置资料连接,表示任意路径都需要ROLE_USER权限 多个权限可以用逗号隔开 -->
<security:intercept-url pattern="/**" access="ROLE_USER"/>
</security:http>
<!-- 相当于不使用数据库读取的密码,在内存中配置用户名和密码还有权限。方便练习 -->
<!-- {noop}是加密方法的名称,后面跟着是变量名(加密后的内容)。框架规定验证时都需要加密。所以需要这样做-->
<!-- 权限名称以ROLE_开头,之后在数据库存储的也需要这样,不然会报错-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="{noop}user"
authorities="ROLE_USER" />
<security:user name="admin" password="{noop}admin"
authorities="ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
到此为止,最简单的功能已经配置完成啦。能够直接运行项目。正常的话会看到权限不足的提示,然后根据配置的用户名登录就好了。
4.完善配置
有了基础配置,那么接下来配置得更细致一点
不使用Spring Security自带的登录页面,添加自己的登录页面
<security:http auto-config="true" use-expressions="false" >
</security:http>
↑这个标签上一步已经写过了
现在要做的是在这个标签加新东西。
<security:http auto-config="true" use-expressions="false" >
<!--网页地址-->
<!--网页表单对应的提交地址-->
<!--用户名对应表单参数-->
<!--密码对应表单参数-->
<!--失败页面-->
<!--成功页面-->
<security:form-login
login-page="/login.html"
login-processing-url="/login"
username-parameter="username"
password-parameter="password"
authentication-failure-url="/failer.html"
default-target-url="/success.html" />
<!-- 登出 删除缓存 -->
<security:logout invalidate-session="true" logout-url="/logout"
logout-success-url="/login.jsp" />
<!-- 关闭CSRF,默认是开启的 防范一种网络攻击方式-->
<security:csrf disabled="true" />
</security:http>
然后自己创建一个页面,填入对应的提交地址,传入对应的用户名和密码,框架会自进行验证。是否和文件里配置的一致。
注意的是需要放行登录页面以及登录失败页面。
<!-- 配置不过滤的资源(静态资源及登录相关) -->
<security:http security="none" pattern="/login.html" />
<security:http security="none" pattern="/failer.html" />
<!-- 对页面用到的对静态资源文件放行 -->
<security:http security="none" pattern="/layui-v2.5.5/layui/layui.js" />
<security:http security="none" pattern="/layui-v2.5.5/layui/css/layui.css" />
5.使用数据库的信息进行验证
SpringSecurity提供:
UserDetails接口 用户的信息 (自带,不需要写)
GrantedAuthority接口 权限 (自带,不需要写)
UserDetailsService接口 判断用户
-
关于UserDetails接口,框架自带一个名叫User的类。我们就不需要动手自己写了。
-
关于GrantedAuthority接口,框架也带有个叫SimpleGrantedAuthority的类,我们也不需要自己写。
当然你想自己动手也行。
框架自带实现UserDetails接口的User类如下:
public class User implements UserDetails, CredentialsContainer {
private static final long serialVersionUID = 500L;
private String password; //密码
private final String username; //用户名
private final Set<GrantedAuthority> authorities; //权限名称集合
private final boolean accountNonExpired; //是否有效
private final boolean accountNonLocked; //是否锁定
private final boolean credentialsNonExpired; //认证是否过期
private final boolean enabled; //是否激活
框架自带的实现了GrantedAuthority接口的SimpleGrantedAuthority的类如下:
public final class SimpleGrantedAuthority implements GrantedAuthority {
private final String role; //权限名称
- 关于UserDetailsService接口,这个接口就只有一个方法。
实际上就是根据自己的数据库,增删改查,
查出自己的User属性,再根据对应属性封装成一个框架的User类。
模板↓
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new User(用户名String,密码String,权限集合Set<GrantedAuthority>);
}
实际上↓
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
TBuserServiceI tBuserService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
TBuser tBuser = tBuserService.queryOneByUserName(s);
Set<GrantedAuthority> roleSet = new HashSet();
for (String role:tBuser.getRoles()
) {
roleSet.add(new SimpleGrantedAuthority(role));
}
User user = null;
if(tBuser!=null){
user = new User(tBuser.getUsername(),tBuser.getPassword(),roleSet);
return user;
}else{
return null;
}
}
代码写好了,就开始配置。记得要在service上标记@Service
接着切换到spring-security.xml 修改先前配置的标签。
因为先前配置,用户名和密码是写死在文件里的,因为现在有数据库了。删掉就行。
在标签里配置属性user-service-ref 对应的UserDetailsService接口实现类
spring-security规定验证用户名和密码时,必须要配置加密类。
加密类就选对应的加密类,这里暂时就选择NoOpPasswordEncoder这个加密类(代表不加密)
配置结果如下:
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsServiceImpl">
<!-- 配置加密的方式 -->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!-- 配置加密类 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/>
6.使用加密类进行信息加密
先前是使用数据库明文存储验证的密码(未加密),现在来改成数据库存储加密后的密码。
spring-security自带了几个加密类。这里使用的是bcrypt加密方式。加密后长度为60位。
在spring-security.xml配置指定的加密类bean。
与第5步类似
只是配置加密类从NoOpPasswordEncoder(不加密)换成BCryptPasswordEncoder(bcrypt加密)
<!-- 配置加密类 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
在spring-security.xml配置加密方式。(与第5步一样的配置)
<security:authentication-manager>
<security:authentication-provider user-service-ref="userDetailsServiceImpl">
<!-- 配置加密的方式 -->
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<!-- 配置加密类 -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder" factory-method="getInstance"/>
存储用户密码的时候也要使用这个加密类
@Autowired
PasswordEncoder passwordEncoder;
@Override
public void add(TBuser tBuser) {
tBuser.setUserid(UUIDUtils.getUUID32());
tBuser.setPassword(passwordEncoder.encode(tBuser.getPassword()));
tBuserMapper.insert(tBuser);
}
存密码的时候,和读取密码的时候,用的都是同一套加密类。所以就能够正常读出来。
数据库中加密前是这样
数据库中加密后就是这样
7.方法级别的权限控制
在此之前,先注意一下名称规范。
官方文档这样说,在权限名称前面需要加ROLE_前缀。例如USER要写成ROLE_USER
不然呢xml文件就会解析错误,我试了,是真的。
官方文档 46.3.3 What does “ROLE_” mean and why do I need it on my role names? >https://docs.spring.io/spring-security/site/docs/5.0.6.RELEASE/reference/htmlsingle/#appendix-faq-role-prefix)
所以不仅仅xml配置权限时需要加前缀。最好统一存储的权限名称时也以ROLE_作为前缀。
错误 <security:intercept-url pattern="/**" access="ADMIN,USER"/>
`正确 <security:intercept-url pattern="/**" access=“ROLE_ADMIN,ROLE_USER”/>
统一了权限名称的格式后 就可以开始配置
确认maven是否添加了JSR250依赖。
<!-- Spring Security方法级权限控制注解-->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
在spring-mvc.xml中配置开启注解权限控制
注意是spring-mvc.xml
注意是spring-mvc.xml
注意是spring-mvc.xml
说三遍!!
<!--开启jsr-250注解的使用-->
<security:global-method-security jsr250-annotations="enabled"/>
神坑:如果在spring-security.xml中配置这玩意,就会报错。
我也不知道为什么 ,大概是我配注解在controller上只有springMVC扫描得到??
报错代码500:An Authentication object was not found in the SecurityContext
在需要控制访问权限的方法前,加上权限控制注解。
@RolesAllowed(“ROLE_ADMIN”) 代表含有ROLE_ADMIN角色的用户可以访问。
@RolesAllowed({“ROLE_ADMIN”,“ROLE_USER”}) 代表多个用户可以访问
@PermitAll 代表任何角色可以访问(但是也需要登录)
@DenyAll 拒绝所有角色访问
例如:允许ROLE_ADMIN用户访问goRegister方法。
@RolesAllowed("ROLE_ADMIN")
@RequestMapping("goRegister")
public ModelAndView goRegister(){
ModelAndView mv = new ModelAndView();
mv.setViewName("user/register");
return mv;
}
配置完毕。再运行项目就会发现,只有ROLE_ADMIN权限的用户可以访问这个方法。
如果想在Controller中获得当前的用户信息怎么办?→可以配置这个Authentication 参数。
authentication代表当前认证对象,可以获得对象信息
@RequestMapping(value = "userInfo",method = RequestMethod.GET )
public ModelAndView userInfo(TBuser tBuser,Authentication authentication){
ModelAndView mv = new ModelAndView();
System.out.println("用户名=="+authentication.getName());
mv.setViewName("/user/userInfo");
return mv;
}