首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Spring Security入门(基于SSM环境配置)

Spring Security入门(基于SSM环境配置)

作者头像
石的三次方
发布2021-01-05 22:25:33
发布2021-01-05 22:25:33
1.5K0
举报
文章被收录于专栏:石的三次方石的三次方

一、前期准备

  • 配置SSM环境

二、不使用数据库进行权限控制

配置好SSM环境以后,配置SpringSecurity环境

  1. 添加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>
  2. 在服务器启动该的时候读取springSecurity配置文件 实现方法,通过application域对象实现,和整合springmybatis的方法相同 <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配置文件。
  • 使用过滤器链拦截所有的资源,来实现对资源的权限控制3

3. 书写springSecurity配置文件

代码语言:javascript
复制
<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中使用

4. 配置成功和失败的处理器 通过实现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这个类是笔者自定义的一个回状态的工具类,具体实现如下
代码语言:javascript
复制
     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);
         }
     }
代码语言:javascript
复制
 - 主要功能,将需要返回的状态信息转换为字符串(字符串转换使用了`Jackson`)

此时一个基本的权限功能已经结束。


三、通过数据库实现权限控制

存在的问题

  • 用户已经写死,需要增加用户只能通过修改xml文件。

解决问题的思路

  • 通过Java代码生成一个User并且赋予它一定的权限
  • 引用入一个接口UserDetailsSevrice重写里面的方法loadUserByUsername
  • 方法返回值是一个UserDetails类型,Spring提供了一个类User实现了UserDetails查看User类的源码
代码语言:javascript
复制
    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;//账户可以使用
  • 创建管理用户权限的类
代码语言:javascript
复制
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对象**

具体实现

  1. 数据库设计(具体情况具体考虑)
  • 三个表:
    • 用户表。存放具体的用户
    • 角色表。存放对应的角色。(管理员,会员,普通用户)
    • 权限表。存放具体的权限。(增加,删除,修改,查询权限的控制)
  • 每一个用户可以有多个角色,一个角色也可以有多个权限
    • 用户和角色。多对多的关系
    • 角色和权限。多对多的关系

2. 编写数据库表的ORM映射,编写持久层接口,服务层接口

3. 创建实现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提供了为密码加密的算法

步骤

  1. 为数据库中的密码加密
  • 通过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);
  1. 登陆时实现解密
  • 创建BCryptPasswordEncoder类的bean对象
  • 在标签中引用
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-12-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 石的三次方 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前期准备
  • 二、不使用数据库进行权限控制
  • 三、通过数据库实现权限控制
  • 四、密码加密
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档