权限控制
一般来说要做权限控制需要在数据库建立三个表:用户表、权限表、角色表。
用户表用来存储用户和每一个用户所对应的角色;
权限表用来存储所有的权限;
角色表是用户所执行的行为需要通过权限来控制;
总结:
用户和角色是多对多
角色和权限是多对多
用户和权限是没有关系,是通过角色表关联在一起
Spring Security简介及入门小Demo
Spring Security是一个能够为基于Spring的企业应用系统提供声明式安全访问控制解决方案的安全框架,它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring中的IOC和DI和AOP功能,为应用系统提供声明式的安全访问控制功能,减少为企业系统安全控制编写大量重复代码的工作。
1.引入坐标
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
2.配置web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<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>
注:
/:不会拦截页面只会拦截路径。
/*:拦截所有的文件夹,不包含子文件夹。
/** :表示的是该目录以及该目录下所有级别子目录的资源。
springSecurityFilterChain的名字必须为这个,不能改变。
3.配置spring-security.xml,此Demo用的是系统自动生成的登录页。
<!-- 页面拦截规则 -->
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login/>
</http>
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="123456" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
user-expressions:是否启用SPEL表达式,默认为true;
pattern:拦截的什么资源
access:创建一个角色,如果启用SPEL,则写成:access=“hasRole(‘ROLE_USER’)”,且角色名称都要以ROLE_开头。
<form-login/>:当前用户必须属于ROLE_USER才能访问的登录页。
<user/>中需要写账号密码和所属的角色。
页面配置:
1.提交方式必须是post;
2.如果使用系统提供的登录页,action="/login";
3.账号和密码的name必须为username和password。
执行流程:
当访问页面时,因为pattern配置的是/**所以进入系统提供的登录页,填写账号密码点击登录时进入此安全登录配置,根据输入的账号密码和配置的账号密码进行匹配,如果是这个账号密码所属的角色,那么就放行,否则输入的账号密码没有对应的角色则不会登录跳转到登录页面。
默认约束规则
自定义登录页
<!-- 放行哪些资源 -->
<http pattren="/login.html" security="none"></http>
<!-- 页面拦截规则 -->
<http use-expressions="false">
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page="/login.html" default-target-url="/index.html"
authentication-failure-url="/login-error.html"
username-parameter="A" password-parameter="B"
login-processing-url="/log" always-use-default-target="true" />
<headers>
<frame-options policy="SAMEORIGIN"/>
<headers/>
<logout logout="/XX" logout-success-url="/YY"/>
</http>
<csrf disabled="true">
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider>
<user-service>
<user name="admin" password="123456" authorities="ROLE_USER"/>
</user-service>
</authentication-provider>
</authentication-manager>
pattren="/login.html":如果不放行登录页面,那么每次进入页面跳转到登录页面进入安全控制之后,由于登录页也需要安全控制,所以再一次进入登录页再一次进入安全控制再一次进入登录页。。。无限循环,会报一个重定向次数过多的错误,所以要放行登录页面。
login-page:自定义的登录页。
default-target-url:登录成功之后跳转到哪里。
authentication-failure-url:登录失败后跳转到哪里。
username-parameter:修改页面上name="username"的username。
password-parameter:修改页面上name="password"的password。
login-processing-url:修改页面中action的跳转的路径名。
<frame-options policy=“SAMEORIGIN”/>:允许使用复合框架。
<csrf disabled=“true”>:跨站请求伪造,一种对网站的恶意利用,SpringSecurity提供这种安全机制,系统自定义的登录页都带有X-CSRF-TOKEN的头信息,为了防止CSRF拦截。需要关闭。
logout:修改页面退出后跳转的路径。
logout-success-url:退出之后跳转到哪个地方。
注:
(1)所有的页面或路径都是以/开头
(2)给<a>连接添加提交功能:在form上加一个id,在<a>上加一个点击事件οnclick=“document:id.submin()”
显示登陆人
@RestController
@RequestMapping("/login")
public class LoginController {
@RequestMapping("name")
public Map name(){
String name=SecurityContextHolder.getContext().getAuthentication().getName();
Map map=new HashMap();
map.put("loginName", name);
return map ;
}
}
(1)@RestController = @ResponseBody+@Controller
(2)路径上加/和不加/的区别:
不带/:相对路径,每次发起请求时,会将路径最后一个地址替换成此次不带/的路径。
带/:看能不能连接到别的项目,如果能连到别的项目,此次请求就是IP+端口+本项目名+此次请求的路径来重定向。如果不能连到别的项目,直接是本项目名+此次请求路径来重定向。
(3)SecurityContextHolder.getContext().getAuthentication().getName()是获取当前通过安全框架登录的人的username。
从数据库中查询账号密码进行安全登录
1.pom.xml中引入依赖
2.web.xml配置
3.由于调用dao是service完成的,所以在web下创建一个service包,编写一个类。
List<GrantedAuthority> grantAuths=new ArrayList();
grantAuths.add(new SimpleGrantedAuthority("ROLE_SELLER"));
//得到商家对象
TbSeller seller = sellerService.findOne(username);
if(seller!=null){
if(seller.getStatus().equals("1")){
return new User(username,seller.getPassword(),grantAuths);
}else{
return null;
}
}else{
return null;
}
构建一个角色的集合,将所以的角色都添加进去,通过传过来的username进行查询,如果查出来不为空且状态为1(已审核过)则就返回账号密码和角色,如果状态不为1或者没有查询出来代表此次登录失败。
4.配置spring-security.xml,用户名和密码都不是写死的了,而是调用编写的类查询数据库。
5.因为springMVC只扫描controller而不扫描service,所以为了程序的健壮性而不把自己刚刚编写的service类放入SpringMVC的Dubbox中扫描,但是只有在远端进了Spring的框架才能拿到服务service进行依赖注入,调用方法访问数据库,所以有以下两个办法解决:
(1)在自己编写的sevice类中调用XXservice,并给与其一个set方法。
然后在spring-security.xml中需要改动或增加的地方:
<http pattern="/seller/add.do" security="none"></http>
<!-- 认证管理器 -->
<authentication-manager>
<authentication-provider user-service-ref="userDetailService">
</authentication-provider>
</authentication-manager>
<!-- 引用dubbo 服务 -->
<dubbo:application name="pinyougou-shop-web" />
<dubbo:registry address="zookeeper://192.168.25.129:2181"/>
<dubbo:reference id="sellerService" interface="com.pinyougou.sellergoods.service.SellerService" >
</dubbo:reference>
<beans:bean id="userDetailService" class="com.pinyougou.service.UserDetailsServiceImpl">
<beans:property name="sellerService" ref="sellerService"></bean:property>
</beans:bean>
放行需要请求的地址,并将自己编写的service放入spring中管理,引入了远程的XXservice。
(2)使用注解的方式进行注入:
<!--认证管理器-->
<authentication-manager>
<!--认证的提供者-->
<authentication-provider user-service-ref="userDetailServiceImpl">
</authentication-provider>
</authentication-manager>
<dubbo:application name="pinyougou-shop-web" />
<dubbo:registry address="zookeeper://192.168.25.153:2181"/>
<dubbo:annotation package="com.pinyougou.service"/>
执行流程:
当页面访问时被此安全框架拦截,由于认证的提供者是自己编写的类所以通过xml配置找到此类,将此类的XXservice从远程注入,如果返回的是账号密码角色则拿着输入的账号密码的角色与此类返回的角色进行对比,如果相同则进入页面,不同则跳转到错误页面,如果类中返回null,找不到输入的账号密码对应的空角色,所以跳转到错误页面。
BCrypt加密算法
MD5:不可逆算法,32位,加盐;
BC算法:可逆算法,64位,加盐;
//密码加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode(seller.getPassword());
seller.setPassword(password);
在进行注册add之前进行密码加密,同时需要在安全框架中配置:
<beans:bean id="bcryptEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
<!-- 认证管理器 -->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref='userDetailService'>
<password-encoder ref="bcryptEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
在工作中常常把加密后的密码再加密18-24次,保证密码的正确性。
其他知识点:
1.通过修改前端JS代码进行条件查询:
ng-init="实体类={属性:'0'}";
前提是后端已经写好了对应的条件判断。
2.@RequestBody使用和不使用的区别
如果使用@RequestBody来接收请求体的json格式数据来封装对象,其请求方式一定要是post,如果不使用@RequestBody,请求方式一定时get,将请求路径上的参数根据名字封装成对象。