一、前期准备
-
配置
SSM
环境
二、不使用数据库进行权限控制
配置好SSM
环境以后,配置SpringSecurity
环境
-
添加
security
依赖<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>4.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>4.1.0.RELEASE</version> </dependency>
-
在服务器启动该的时候读取
springSecurity
配置文件实现方法,通过application域对象实现,和整合
spring
和mybatis
的方法相同<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:springSecurity.xml </param-value> </context-param> <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
提供的监听器加载security
配置文件。 -
使用过滤器链拦截所有的资源,来实现对资源的权限控制
-
-
书写
springSecurity
配置文件
<security:http> <security:intercept-url pattern="add" access="hasAuthority('ROLE_USER')" /> <security:intercept-url pattern="index.jsp" access="permitAll()"/> <security:intercept-url pattern="/login" access="permitAll()"/> <security:intercept-url pattern="/*" access="isFullyAuthenticated()"/> <security:form-login login-page="/login" login-processing-url="/login" authentication-failure-handler-ref="failureHandler" authentication-success-handler-ref="successHandler"> </security:form-login> <security:csrf disabled="true"></security:csrf> </security:http> <security:authentication-manager> <security:authentication-provider> <security:user-service> <security:user name="username" password="username" authorities="ROLE_USER"> </security:user> </security:user-service> </security:authentication-provider> </security:authentication-manager> <bean id="successHandler" class="com.bywlstudio.security.SuccessHandler"></bean> <bean id="failureHandler" class="com.bywlstudio.security.FailureHandler"></bean>
-
security:http
配置权限拦截的方式是基于HTTP
的 -
资源拦截方式,有两种
-
security:form-login
基于表单(常用)-
security
默认提供一个登陆界面,可以自定义 -
login-page
指定登陆界面(注:用户名的name
属性必须为username
,密码的name
属性必须为password
。这是security
判断用户输入是否正确的标准) -
login-processing-url
指定登陆界面的表单的提交路径 -
authentication-failure-handler-ref
引用一个密码错误以后的处理方式(上面的使用了bean
引用) -
authentication-success-handler-ref
应用一个成功以后的处理方式
-
-
security:http-basic
基本的验证方式(不常用)
-
-
secutity:authentication-manager
具体的权限管理配置 -
security:intercept-url
-
access
配置可以访问的权限(取值参考:security官方文档) -
pattern
配置需要拦截的资源(上面的资源使用MVC
控制)
-
-
security:csrf
一种浏览器的防护机制,后期文章会详细说明 -
security:authentication-provider
具体的实现权限控制 -
security:user
配置一个具体的用户-
authorities
配置当前用户所具有的权限,在intercept-url
中使用
-
-
配置成功和失败的处理器
通过实现
AuthenticationSuccessHandler
接口和AuthenticationFailureHandler
实现public class FailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { response.getWriter().write(WriteStatusJson.loginStatus("status","Failure")); } } public class SuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.getWriter().write(WriteStatusJson.loginStatus("status","success")); } }
-
WriteStatusJson
这个类是笔者自定义的一个回状态的工具类,具体实现如下public class WriteStatusJson { /** * 返回登陆状态信息 * @param attritute 登陆的状态 * @param value 登陆的成功过或者失败的返回值 * @param <E> 根据每一次状态返回的值而定 * @return {String} 返回一个json字符串 * @throws JsonProcessingException */ public static <E> String loginStatus(String attritute , E value) throws JsonProcessingException { ObjectMapper objectMapper = new ObjectMapper(); Map<String,E> map = new HashMap<>(); map.put(attritute,value); return objectMapper.writeValueAsString(map); } }
-
主要功能,将需要返回的状态信息转换为字符串(字符串转换使用了
Jackson
)
-
-
此时一个基本的权限功能已经结束。
三、通过数据库实现权限控制
存在的问题:
-
用户已经写死,需要增加用户只能通过修改
xml
文件。
解决问题的思路
-
通过
Java
代码生成一个User
并且赋予它一定的权限 -
引用入一个接口
UserDetailsSevrice
重写里面的方法loadUserByUsername
-
方法返回值是一个
UserDetails
类型,Spring
提供了一个类User
实现了UserDetails
查看User
类的源码
private static final long serialVersionUID = 410L;//序列化 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;//账户可以使用
-
创建管理用户权限的类
public class UserAuthent implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { User user = new User("MakerStack","love", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ADMIN")); return user; } }
-
AuthorityUtils
工具类给用户赋予权限,类似于xml
中的authorities
属性
存在的问题
-
用户还是写死的,只能用过
spring
自带的User对象来赋值,不能连接数据库
解决思路
-
上面提到
spring
自带的User
通过实现了UserDetails
接口来实现对用户权限的添加 -
自定义
User
类实现UserDetails
接口,创建操纵User
类的持久层接口,获取数据库中的User
对象**
具体实现
-
数据库设计(具体情况具体考虑)
-
三个表:
-
用户表。存放具体的用户
-
角色表。存放对应的角色。(管理员,会员,普通用户)
-
权限表。存放具体的权限。(增加,删除,修改,查询权限的控制)
-
-
每一个用户可以有多个角色,一个角色也可以有多个权限
-
用户和角色。多对多的关系
-
角色和权限。多对多的关系
-
-
-
编写数据库表的
ORM
映射,编写持久层接口,服务层接口 -
创建实现
UserDetailsService
接口的类,实现方法User user = userService.findUserByUsername(username); if(user!=null){ List<Authority> authorities = userService.findAuthorityByUsername(username); //需要对user的List<Authority>集合赋值 List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); for (Authority authority : authorities) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authority.getTag()); grantedAuthorities.add(grantedAuthority); } user.setAuthorities(grantedAuthorities); } return user;
-
方法
loadUserByUsername
方法的参数即为用户在表单中输入的用户名(后期会出一篇security
执行流程的文章,可以关注一下) -
通过用户名获取对应的用户信息,判断用户是否存在,存在即获取其对应的权限信息;若不存在则直接返回
-
实体类
User
实现了UserDetails
接口,内部定义了一个存储用户权限的集合。现在需要做的就是将数据库中的权限信息添加到这个集合中。 -
通过
spring
提供的接口GrantedAuthority
来实现;通过其子类SimpleGrantedAuthority
将数据库中的权限信息写入,赋值给GrantedAuthority
类型,添加到一个集合中,将这个集合赋值给user
类的权限集合数据库整合完成
-
需要注意的问题
-
登陆界面的
name
属性必须保证,否则会出现loadUserByUsername
方法获取不到参数的问题-
用户名为
username
-
密码为
password
-
否则
security
无法读取
-
-
若有读者出现这个错误
-
Access denied for user 'root'@'localhost'(using password: YES)
且你之前连接数据库是正确的,则检查你得数据库配置文件
-
四、密码加密
目前数据库中存放的密码均为明文传输,所以需要对密码进行加密,而
security
提供了为密码加密的算法
步骤
-
为数据库中的密码加密
-
通过
security
提供的接口PasswordEncoder
的实现类BCryptPasswordEncoder
实现用户密码的加密和匹配 -
接口
PasswordEncoder
中的方法
String encode(CharSequence var1); boolean matches(CharSequence var1, String var2);
-
实现加密
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); //哈希算法+变量 String encode = passwordEncoder.encode("123456"); User user = new User(); user.setId(3); user.setPassword(encode); userService.updateUserPassword(user);
-
-
登陆时实现解密
-
创建
BCryptPasswordEncoder
类的bean
对象 -
在
<security:authentication-proider>
标签中引用
-