Spring Secirty的使用

1.介绍

Spring Security是 Spring提供的安全认证服务的框架。 使用Spring Security可以帮助我们来简化认证
和授权的过程
官网: https://spring.io/projects/spring-security.

2.配置

2.1.Maven文件坐标

<dependency> 
	<groupId>org.springframework.security</groupId> 		    
	<artifactId>spring-security-web</artifactId> 
	<version>5.0.5.RELEASE</version> 
</dependency> 
<dependency> 
	<groupId>org.springframework.security</groupId> 
	<artifactId>spring-security-config</artifactId> 
	<version>5.0.5.RELEASE</version> 
</dependency>

2.2.配置Mvc中的web.xml文件

在web.xml中主要配置SpringMVC的DispatcherServlet和用于整合第三方框架的DelegatingFilterProxy,用于整合Spring Security。

<filter>
	<!--
	DelegatingFilterProxy用于整合第三方框架
	整合Spring Security时过滤器的名称必须为springSecurityFilterChain 
	否则会抛出NoSuchBeanDefinitionException异常
	-->
	<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>
<servlet>
	<servlet-name>servlet的别名A</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<!--指定加载的配置文件,通过参数contextConfigLocation加载-->
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:spring-security.xml</param-value>
	</init-param>
	<!--
	load-on-startup表示servlet应该被载入的顺序
	当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet
	当值小于0或者没有指定时,则表示容器在该servlet被选择时才会去加载
	-->
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>servlet的别名A</servlet-name>
	<!--
	*.xxx扩展名匹配,这里表示匹配全部以do结尾的请求
	-->
	<url-pattern>*.do</url-pattern>
</servlet-mapping>

2.3 .配置spring-security.xml

在spring-security.xml中主要配置Spring Security的拦截规则认证管理器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
						http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/mvc
						http://www.springframework.org/schema/mvc/spring-mvc.xsd
						http://code.alibabatech.com/schema/dubbo
						http://code.alibabatech.com/schema/dubbo/dubbo.xsd
						http://www.springframework.org/schema/context
						http://www.springframework.org/schema/context/spring-context.xsd
                     http://www.springframework.org/schema/security
                     http://www.springframework.org/schema/security/spring-security.xsd">

	<!--
		http:用于定义相关权限控制 
		auto-config:是否自动配置 
			设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出 处理等 
			设置为false时需要显示提供登录表单配置,否则会报错 
		use-expressions:用于指定intercept-url中的access属性是否使用表达式 
	-->
	<security:http auto-config="true" use-expressions="true">
		<!--
			intercept-url: 定义一个拦截规则
			pattern: 对哪些url进行权限控制
			access: 在请求对应的url时需要什么权限,默认配置时它应该是一个以,分隔的角色列表
				请求的用户只需要拥有其中一个角色就能成功访问到对应的url	
		-->
		<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
			<!--access配置所有的属性值
			hasIpAddress(ipAddress): 用于匹配一个请求的ip地址或者一个地址的网络掩码
				例:access="hasIpAddress('162.0.0.0/224')"
			hasRole(role): 用于匹配一个使用GrantedAuthority(已授予的权限)的角色(类似RoleVoter)
				RoleVoter是Spring Security内置的一个AccessDecisionVoter
					(AccessDecisionVoter 是一个投票器,负责对授权决策进行表决。
					然后,最终由唱票者AccessDecisionManager 统计所有的投票器表决后,来做最终的授权决策。)
				例:access="hasRole('ROLE_USER')"
			hasAnyRole(role): 用于匹配一个使用GrantedAuthority的角色列表。用户匹配其中任何一个均可放行
				例:access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"
		-->
	</security:http>
	
	<!--
	 authentication-manager: 认证管理器,用于处理认证操作
	-->
	<security:authentication-manager>
		<!--
			authentication-provider: 认证提供者,执行具体的认证逻辑
		-->
		<security:authentication-provider>
			<!--
				user-service: 用于获取用户信息,提供给authentication-provider进行认证
			-->
			<security:user-service>
				<!--
					user: 定义用户信息,可以指定用户名、密码、角色,
						(也可以直接从数据库中查询用户信息不用定义)
					{noop}: 表示当前使用的密码为明文
					authorities: 表示这个用户所拥有的角色
				-->
				<security:user name="admin"
							   password="{noop}admin"
							   authorities="ROLE_ADMIN">
				</security:user>
			</security:user-service>
		</security:authentication-provider>
	</security:authentication-manager>
<beans>	

2.3.1.spring-security.xml的改进地方

2.3.1.1.配置可匿名访问的资源

不是所有的url我们都进行拦截加上权限控制,有些url我们认为它可以匿名访问(无需登录就能访问)

<!--
配置哪些资源匿名可以访问(不登录也可以访问),
这里我们定义一个登录页面可以匿名访问
///
这里我是配置在auto-config之前
-->
<security:http security="none" pattern="/login.html"></security:http>
<security:http security="none" pattern="/css/**"></security:http>
<security:http security="none" pattern="/js/**"></security:http>

2.3.1.2.我们需要一个专门的登录页面

我们需要一个专门的登录页面

<!--
对应上面的匿名访问的login.html,这里我们自定义一个简陋的登录页面(这个还是得看前端大爷的)
-->

<html>
<head>
	<title>登录页面</title>
</head>
<body>
	<form action="/login.do" method="post">
		username: <input type="text" name="username"><br>
		password: <input type="password" name="password"><br>
		<input type="submit" value="submit">
	</form>
</body>
</html>
2.3.1.3.这个对登录和退出登录的配置修改
<!--
        auto-config:自动配置,如果设置为true,表示自动应用一些默认配置,比如框架会提供一个默认的登录页面
        use-expressions:是否使用spring security提供的表达式来描述权限
-->
<security:http auto-config="true" use-expressions="true">

	<security:headers>
            <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
            <security:frame-options policy="SAMEORIGIN"></security:frame-options>
    </security:headers>
        <!--配置拦截规则,/** 表示拦截所有请求-->
        <!--
            pattern:描述拦截规则
            asscess:指定所需的访问角色或者访问权限
        -->
	<!--只要认证通过就可以访问-->
    <security:intercept-url pattern="/pages/**"  access="isAuthenticated()" />
        
	<!--如果我们要使用自己指定的页面作为登录页面,必须配置登录表单.页面提交的登录表单请求是由框架负责处理-->
        <!--
            login-page: 指定登录页面访问URL
            login-processing-url: 我们自定义的登录用户验证路径
            default-target-url: 身份验证登陆成功后发送的请求
            authentication-failure-url : 身份验证登陆失败后发送的请求
        -->
    <security:form-login
            login-page="/login.html"
            username-parameter="username"
            password-parameter="password"
            login-processing-url="/login.do"
            default-target-url="/index.html"
            authentication-failure-url="/login.html"></security:form-login>
	<!--
          csrf:对应CsrfFilter过滤器
          disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项
         (配置 disabled="true"就行了),否则登录操作会被禁用(403)
     -->
    <security:csrf disabled="true"></security:csrf>

    <!--
		logout:退出登录
		logout-url:退出登录操作对应的请求路径
		logout-success-url:退出登录后的跳转页面
		invalidate-session:否销毁Session 
    -->
    <security:logout logout-url="/logout.do"
                logout-success-url="/login.html"
                invalidate-session="true"/>

</security:http>
2.3.1.4.从数据库中读取用户数据

之前是直接将用户名和密码配置在了配置文件中,而实际生活中我们的用户名和密码往往保存在数据库

如果我们要从数据库动态查询用户信息,就必须按照spring security框架的要求提供一个实现UserDetailsService接口的实现类,并按照框架的要求进行配置即可。框架会自动调用实现类中的方法并自动进行密码校验。
1.创建实体类

public class User implements Serializable{
    private Integer id; // 主键
    private Date birthday; // 生日
    private String gender; // 性别
    private String username; // 用户名,唯一
    private String password; // 密码
    private String remark; // 备注
    private String station; // 状态
    private String telephone; // 联系电话
    private Set<Role> roles = new HashSet<Role>(0);//对应角色集合
为了节省篇幅,省去了set,get方法
}

2.自定义的SpringSecurityUserService 实现UserDetailsService接口,来完成授权和验证

@Component
public class SpringSecurityUserService implements UserDetailsService {
    @Reference
    //注意这里使用dubbo远程调用用户服务
    private UserService userService;
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		//远程调用用户服务,根据用户名查询用户信息
        User user = userService.findByUsername(username);
        if (user != null){

            //非空,则说明用户存在
            //通过用户获取角色
            Set<Role> roles = user.getRoles();
            //创建权限集合,方便调用
            List<GrantedAuthority> list = new ArrayList<>();
            //遍历所有的用户角色
            for (Role role : roles) {
            //授予角色
             	//keyword; 权限关键字,用于权限控制
                list.add(new SimpleGrantedAuthority(role.getKeyword()));
                //获取角色的权限
                Set<Permission> permissions = role.getPermissions();
                //遍历所有的权限
                for (Permission permission : permissions) {
				// 把权限注入到角色中(授权)
                 list.add(new SimpleGrantedAuthority(permission.getKeyword()));
                 //   keyword; 权限关键字,用于权限控制
                    //SimpleGrantedAuthority是基于
                    // GrantedAuthority权限的授权方式
                }
            }
            org.springframework.security.core.userdetails.User securityUser = new org.springframework.security.core.userdetails.User(username,user.getPassword(),list);
            return securityUser;
        }else {
            return null;
        }
    }
}

3.1UserDao层以及sql语句

public interface UserDao {
//    通过用户名查询用户id(通过用户名判断是否存在用户,有id就有用户,没有id就没有用户)
    public User findByUsername(String username);
}
//分开的,sql语句放在resources包下的UserDao.xml中
<mapper namespace="com.dao.UserDao">
    <select id="findByUsername" parameterType="string" resultType="com.POJO.User">
        select * from t_user where username = #{username}
    </select>
</mapper>

3.2RoleDao以及sql语句

public interface RoleDao {
    //通过用户id把角色赋给相应的用户
    (这里角色相当于职位,permission权限,相当于其(role)职位的权利(权限)
    public Set<Role> findRoleByUserId(Integer userId);
}
//sql语句
<mapper namespace="com.dao.RoleDao">
    <select id="findRoleByUserId" parameterType="int" resultType="com.POJO.Role">
          SELECT r.*  FROM t_role r , t_user_role ur WHERE r.id = ur.role_id AND ur.user_id = #{user_id}
    </select>
</mapper>

3.3PermissionDao以及sql语句

public interface PermissionDao {
    //给相应的角色(可以理解为职位),赋予相应的权利
    public Set<Permission> findPermissionByRoleId(Integer roleId);
}
//sql语句
<mapper namespace="com.dao.PermissionDao">
    <select id="findPermissionByRoleId" parameterType="int" resultType="com.POJO.Permission">
        SELECT p.* FROM t_permission p , t_role_permission rp
	WHERE p.id = rp.permission_id AND rp.role_id = #{permission_id}
    </select>
</mapper>

4.service层以及其实现类

public interface UserService {
    public User findByUsername(String username);
}
// UserService的实现类
@Service(interfaceClass = UserService.class)
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private PermissionDao permissionDao;
    public User findByUsername(String username) {
        //通过username查询,来确认是否存在用户
        User user = userDao.findByUsername(username);
        if (user != null){
            //user不为null,说明user存在
            //通过用户获取userId,以此查询角色(相当于职位)和permission相应权限
            //即,根据用户ID查询对应的角色
            Integer userId = user.getId();
            Set<Role> roles = roleDao.findRoleByUserId(userId);
            user.setRoles(roles);//让用户关联角色
            if (roles != null){
                for (Role role : roles) {
                    //循环遍历角色
                    Integer roleId = role.getId();
                    //根据角色ID查询关联的权限
                    Set<Permission> permissions = permissionDao.findPermissionByRoleId(roleId);
                    //让角色关联相应权限
                    role.setPermissions(permissions);
                }
            }
        }else {
            return null;
        }
        return user;
    }
}

5.cotroller层代码

@RestController
@RequestMapping("/user")
public class UserController {
    //获取当前登录(认证)用户的用户名
    @RequestMapping("/getLoginUsername")
    public Result getLoginUsername(){
        try{
        //通过Authentication.getPrincipal()可以获取到代表当前用户的信息,
        //这个对象通常是UserDetails的实例。获取当前用户的用户名是一种比较常见的需求。
		//此外,调用SecurityContextHolder.getContext()是获取SecurityContext,
		//如果对应的SecurityContext不存在,则Spring Security将为我们建立一个空的SecurityContext并进行返回。
            User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();//获取用户信息
            String username = user.getUsername();
            String password = user.getPassword();
            Collection<GrantedAuthority> authorities = user.getAuthorities();
            return new Result(true, MessageConstant.GET_USERNAME_SUCCESS,username);
        }catch (Exception e){
            return new Result(false, MessageConstant.GET_USERNAME_FAIL);
        }
    }
}

2.1.1.5由于{noop}的缘故,密码是明文状态,并不安全,正常都是加密的
<!--配置认证管理器-->
<security:authentication-manager>
	<!--更改Spring-Security.xml中身份验证的方式,使用自定义的UserDetailsService-->
    <security:authentication-provider user-service-ref="springSecurityUserService">
        <!--指定密码加密策略-->
        <security:password-encoder ref="passwordEncoder" />
    </security:authentication-provider>
</security:authentication-manager>

<!--配置密码加密对象-->
<bean id="passwordEncoder"
      class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

<!--开启注解方式权限控制-->
<security:global-method-security pre-post-annotations="enabled" />

开启注解方式后,我们就能通过添加@PreAuthorize(“hasAuthority(‘xxx权限’)”)注解来权限校验

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值